home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / v10n01.arc / PCSPOOL.ASM < prev    next >
Assembly Source File  |  1990-12-11  |  130KB  |  4,001 lines

  1.         page    55,132
  2.         title   "PCSPOOL - Print Spooler"
  3.  
  4. svc_charq   equ     500/18            ; dflt chars per service
  5. svc_testq   equ     500             ; dflt nbr of tests per char
  6.  
  7. lpt_dqsz    equ     1024            ; deque buffer size
  8.  
  9. ; --------------------------------------------
  10. ; definition of structure for a single printer
  11. ; --------------------------------------------
  12.  
  13. lpstruc     struc
  14. lpprtnbr    dw        ?                ; printer number
  15. lpstatport  dw        0                ; printer status port addr
  16. lpfirstqr   dw        0                ; first record on queue
  17. lplastqr    dw        0                ; last record on queue
  18.  
  19. lpprted     dd        0                ; characters actually prt'd
  20. lpinque     dd        0                ; characters in spool
  21.  
  22. lpdqbuf     dd        0                ; pointer to dq buf (puddle)
  23. lplast        dd        0                ; last cnt of prt'd chars
  24. lpticks     dd        0                ; nbr ticks for prt'd chrs
  25. lpdqin        dw        0                ; offset of next char to put
  26.                         ; .. updated by main
  27. lpdqout     dw        0                ; offset of next char to prt
  28.                         ; .. updated by toprt
  29.  
  30. lpwrkqr     dw        0                ; pointer to spooling qrec
  31. lpspools    db        90h             ; current spooling status
  32. lpstatus    db        0feh            ; current printer status
  33.                         ;  00 = ok
  34.                         ;  01 = not ready (hardware)
  35.                         ;  02 = paused w/message
  36.                         ;  03 = paused
  37.                         ;  04 = waiting init'ing
  38.                         ;  fe = non-existent port
  39.                         ;  ff = not currently spool
  40. lpcontrol   db        0                ; current ctrl record type
  41. lpcps        dw        0                ; observed printer speed
  42. lpchars     dw        svc_charq            ; chars to send per service
  43. lpwrite     db        0                ; 1 = write need
  44. lpflush     db        0                ; 1 = flush all queued data
  45. lptmrflg    db        0                ; 1 = update cps stats
  46. lpstruc     ends
  47.  
  48. ; --------------------------------
  49. ; definition of queue record entry
  50. ; --------------------------------
  51.  
  52. qrlen        equ     512             ; length of a qrec entry
  53.                         ;  note: a change from 512
  54.                         ;     requires code change
  55.                         ;     in getmemz routine
  56. qrdlen        equ     qrlen - 5            ; data length
  57.  
  58. qrstruc     struc
  59. qrnextqr    dw        ?                ; next record on queue
  60. qrcontrol   db        ?                ; control byte
  61.                         ;  00: data
  62.                         ;  01: data, next rec is ctl
  63.                         ;  02: display qrdata, pause
  64.                         ;  04: initialize printer
  65. qrbytes     dw        ?                ; bytes in record
  66. qrdata        db        qrdlen dup (?)        ; remainder is data
  67. qrstruc     ends
  68.  
  69. cgroup        group   mainline, convq, diskq
  70.  
  71. mainline    segment para public 'code'      ; mainline
  72.         assume  cs:mainline, ds:mainline, es:mainline, ss:mainline
  73.         org     100h
  74.  
  75. begin:        jmp     start            ; start processing
  76.  
  77. ; --------------------------------------------------------
  78. ;   This is the area where the queue header will be loaded
  79. ; --------------------------------------------------------
  80.  
  81. qheader     label   byte            ; q headers area
  82. qhvalid     dw        0                ; signature byte
  83. qhfirstf    dw        0                ; first free buffer
  84. qhlpt1        lpstruc <0>             ; structure for lpt1
  85. qhlpt2        lpstruc <1>             ; structure for lpt2
  86. qhlpt3        lpstruc <2>             ; structure for lpt3
  87.  
  88. lplen        equ     qhlpt2 - qhlpt1        ; length of lp structure
  89. qhlen        equ     2+(lplen*3)         ; queue header length
  90.  
  91. ; --------------------------
  92. ;   interrupt vector control
  93. ; --------------------------
  94.  
  95. intstksz    equ     32           ;; debug ;;  ; size of interrupt stacks
  96.  
  97. intblkcnt   equ     6                ; number of int routines
  98. intblklen   equ     15                ; length of blocks
  99. intblk1     equ     offset oldint13        ; first to fill in
  100.  
  101. oldint13    dd        0                ; old cs:ip
  102. oldss13     dd        0                ; ss:sp when int invoked
  103. newss13     dd        0                ; work stack during int
  104.         db        13h             ; interrupt to take over
  105.         dw        offset int13        ; new interrupt offset
  106.  
  107. oldint09    dd        0                ; old cs:ip
  108. oldss09     dd        0                ; ss:sp when int invoked
  109. newss09     dd        0                ; work stack during int
  110.         db        09h             ; interrupt to take over
  111.         dw        offset int09        ; new interrupt offset
  112.  
  113. oldint17    dd        0                ; old cs:ip
  114. oldss17     dd        0                ; ss:sp when int invoked
  115. newss17     dd        0                ; work stack during int
  116.         db        17h             ; interrupt to take over
  117.         dw        offset int17        ; new interrupt offset
  118.  
  119. oldint08    dd        0                ; old cs:ip
  120. oldss08     dd        0                ; ss:sp when int invoked
  121. newss08     dd        0                ; work stack during int
  122.         db        08h             ; interrupt to take over
  123.         dw        offset int08        ; new interrupt offset
  124.  
  125. oldint21    dd        0                ; old cs:ip
  126. oldss21     dd        0                ; ss:sp when int invoked
  127. newss21     dd        0                ; work stack during int
  128.         db        21h             ; interrupt to take over
  129.         dw        offset int21        ; new interrupt offset
  130.  
  131. oldint28    dd        0                ; old cs:ip
  132. oldss28     dd        0                ; ss:sp when int invoked
  133. newss28     dd        0                ; work stack during int
  134.         db        28h             ; interrupt to take over
  135.         dw        offset int28        ; new interrupt offset
  136.  
  137. svc_chars   dw        svc_charq            ; chars per service
  138.  
  139. pcsflg        db        0                ; flags
  140. pcsflgl1    equ     01h             ; .... ...1 init: spool lpt1
  141. pcsflgl2    equ     02h             ; .... ..1. init: spool lpt2
  142. pcsflgl3    equ     04h             ; .... .1.. init: spool lpt3
  143. pcsflgcm    equ     08h             ; .... 1... conv memory
  144. pcsflgdk    equ     10h             ; ...1 .... disk spooling
  145. pcsflgem    equ     20h             ; ..1. .... ems (future)
  146.  
  147. pcsflgsx    equ     38h             ; all bits for spool type
  148.  
  149. qrtn        dd        0                ; queue routine
  150. qrtnlen     dw        0                ; .. len of routine
  151. qrtnseg     equ     word ptr qrtn+2        ; .. segment of queue rtn
  152.  
  153. dqrec        dw        0                ; ptr to work dq record
  154.  
  155. qfile        db        "C:\"                   ; default drive/directory
  156.         db        65 dup (0)
  157.  
  158. nextqrnbr   dw        1                ; next queue record nbr
  159.  
  160. memqseg     dw        0                ; segment of memory spool
  161. memqsize    dw        16                ; K size (init)  recs (run)
  162.  
  163. nxtavail    dw        qhandler            ; next available byte
  164. startclr    dw        0                ; addr to start mem clear
  165. clrlen        dw        0                ; length to clear
  166.  
  167. dos_busy    dd        0                ; addr of dos busy flag
  168. i28hflag    db        0                ; used by DOS Waiting rtn
  169. intsbusy    db        0                ; interrupts busy flag
  170. i08hflg     dw        0                ; int 08 in use
  171. i17hflg     db        0                ; int 17 in use (part)
  172. chkprtflg   db        0                ; chkprt rtn in use
  173. qinuseflg   db        0                ; queue in use flag
  174. close_req   db        0                ; close requested flag
  175.                         ;  0 = not requested
  176.                         ;  1 = close requested
  177.                         ;  2 = close completed
  178.  
  179. i21hufh     dw        0                ; user's file handle
  180. i21hlen     dw        0                ; remaining length
  181. i21hflg     db        1                ; in use flag
  182.  
  183. tsr_active  db        0                ; tsr active flag
  184. tsr_req     db        0                ; tsr request flag
  185. tsr_cnt     db        0                ; tsr tick counter
  186.  
  187.         db        "Hot key>"              ; visual ptr for hot key
  188. shift_mask  db        0ch             ; ..and shift mask
  189.                         ; .... ...1 right shift
  190.                         ; .... ..1. left shift
  191.                         ; .... .1.. control key
  192.                         ; .... 1... alt key
  193. hot_key     db        19h             ; scan code of hotkey
  194.  
  195. ; ---------------------------------------------------------------------
  196. ; screen management items
  197. ; ---------------------------------------------------------------------
  198.  
  199. lines        equ     5                ; 5 lines
  200. cols        equ     80                ; .. of 80 columns
  201.  
  202. monoattr    equ     07h             ; monochrome attribute
  203. colattr     equ     1fh             ; color attribute
  204.  
  205. ourattr     db        07h             ; attr to use, default mono
  206.  
  207.                         ; display status messages
  208.                         ;    (lpstatus, length, msg)
  209. dstat_msgs  db        0, 43, ' xxx% **********  sssK  nnnK  mmmK hh:mm:ss'
  210.         db        1, 18, ' Printer not ready'
  211.         db        2,    9, ' Paused: '
  212.         db        3, 27, ' Paused, use GO to continue'
  213.         db        4, 23, ' Waiting initialization'
  214.         db        5, 15, ' Flushing queue'
  215.         db     0feh, 17, ' No printer found'
  216.         db           22, ' Not currently spooled'
  217.  
  218. dstat_nbr   equ     7                ; nbr of messages - 1
  219.  
  220.  
  221.  
  222. ; --------------------------------------------------------------------
  223. ;   This routine checks the buffer for the printers and sends the info
  224. ;   out via the original INT17 routine
  225. ; --------------------------------------------------------------------
  226.  
  227. toprt        proc                ; send to printer
  228.         push    ax                ; save registers
  229.         push    bx
  230.         push    cx
  231.         push    dx
  232.         push    si
  233.         push    ds
  234.         push    es
  235.  
  236.         push    cs                ; save our segment addr
  237.         pop     ds                ; ds -> our segment
  238.  
  239.         mov     bx, offset qhlpt1        ; bx -> lpt1 control block
  240.  
  241. toprt10:    cmp     [bx].lpstatus, 01h        ; q. available for print?
  242.         jbe     toprt15            ; a. yes .. do something
  243.  
  244.         cmp     [bx].lpstatus, 4        ; q. need initialization?
  245.         je        toprt12            ; a. yes .. handle w/BIOS
  246.         jmp     toprt80            ; else .. do next printer
  247.  
  248. toprt12:    mov     ah, 1            ; ah = init printer fnc
  249.         mov     dx, [bx].lpprtnbr        ; dx -> printer nbr
  250.         pushf                ; .. save our flags
  251.         call    oldint17            ; call old int 17 routine
  252.  
  253.         mov     [bx].lpstatus, 0        ; make printer "ready"
  254.         mov     [bx].lpcontrol, 0        ; ..for next queue record
  255.         jmp     short toprt80        ; ..continue w/common code
  256.  
  257. toprt15:    mov     ax, [bx].lpdqin        ; ax = input offset
  258.  
  259.         cmp     ax, [bx].lpdqout        ; q. anything to print?
  260.         je        toprt80            ; a. no .. try again later..
  261.  
  262.         mov     ax, svc_chars        ; ax = chars per service
  263.         mov     [bx].lpchars, ax        ; .. reset chars this svc
  264.  
  265. toprt20:    mov     cx, svc_testq        ; cx = nbr of times to try
  266.         mov     dx, [bx].lpstatport     ; dx = status port addr
  267.  
  268. toprt22:    in        al,dx            ; pre-charge +busy line (?)
  269.         in        al,dx            ; al = prt port status
  270.  
  271.         test    al, 10h            ; q. printer selected?
  272.         jnz     toprt24            ; a. yes .. check for ready
  273.  
  274.         mov     [bx].lpstatus, 1        ; else .. hardware problem
  275.         jmp     short toprt80        ; .. check next printer
  276.  
  277. toprt24:    mov     [bx].lptmrflg, 1        ; increment CPS stats
  278.         test    al, 80h            ; q. printer ready?
  279.         jnz     toprt25            ; a. yes.. print a char
  280.         loop    toprt22            ; else .. try again
  281.         jmp     short toprt80        ; done trying? .. nxt prt
  282.  
  283. toprt25:    mov     dx, [bx].lpprtnbr        ; dx -> printer nbr
  284.         mov     [bx].lpstatus, 0        ; show printer available
  285.  
  286.         push    bx                ; save lp ptr
  287.         xor     ah, ah            ; ah = print a char
  288.         les     si, [bx].lpdqbuf        ; es:si -> memory puddle
  289.         mov     bx, [bx].lpdqout        ; bx -> next char to print
  290.         mov     al, es:[bx+si]        ; al = char to print
  291.         inc     bx                ; bx -> next char
  292.  
  293.         cmp     bx, lpt_dqsz        ; q. bx beyond max?
  294.         jb        toprt30            ; a. no .. continue
  295.  
  296.         xor     bx, bx            ; else .. reset pointer
  297.  
  298. toprt30:    mov     cx, bx            ; cx = new offset
  299.         pop     bx                ; bx -> lpt block
  300.         mov     [bx].lpdqout, cx        ; save new buffer offset
  301.  
  302.         pushf                ; .. save our flags
  303.         call    oldint17            ; call old int 17 routine
  304.  
  305.         add     word ptr [bx].lpprted, 1    ; increment printed cnt
  306.         adc     word ptr [bx].lpprted+2, 0
  307.  
  308.         cli                  ; hold interrupts
  309.         sub     word ptr [bx].lpinque, 1     ; dec chars left
  310.         sbb     word ptr [bx].lpinque+2, 0     ; . . . . .
  311.         sti                  ; re-enable ints
  312.  
  313.         cmp     [bx].lpdqin, cx        ; q. any chars to print?
  314.         je        toprt80            ; a. no .. exit
  315.  
  316.         dec     [bx].lpchars        ; q. enough chars sent?
  317.         jnz     toprt20            ; a. no .. send some more
  318.  
  319. toprt80:    add     bx, lplen            ; bx -> next lp block entry
  320.  
  321.         cmp     bx, offset qhlpt3        ; q. last printer serviced?
  322.         ja        toprt90            ; a. yes .. exit loop
  323.         jmp     toprt10            ; else .. do another printer
  324.  
  325. toprt90:    pop     es                ; restore registers
  326.         pop     ds
  327.         pop     si
  328.         pop     dx
  329.         pop     cx
  330.         pop     bx
  331.         pop     ax
  332.  
  333.         ret                 ; return to caller
  334. toprt        endp
  335.  
  336.  
  337. ; ----------------------------------------------------------------------
  338. ;   This routine handles the users attempt to print using BIOS interrupt
  339. ;   17h.  Output is queued to the printer if ready, else buffered then
  340. ;   spooled.
  341. ;
  342. ;   Standard int 17 Calls
  343. ;   ---------------------
  344. ;
  345. ;   Send Character to Printer
  346. ;    ah = 00h, funtion code
  347. ;    al = character to print
  348. ;    dx = printer number
  349. ;
  350. ;     Returns
  351. ;    ah = printer status
  352. ;
  353. ;
  354. ;   Initialize Printer
  355. ;    ah = 01h, funtion code
  356. ;    dx = printer number
  357. ;
  358. ;     Returns
  359. ;    ah = printer status
  360. ;
  361. ;
  362. ;   Get Printer Status
  363. ;    ah = 02h, funtion code
  364. ;    dx = printer number
  365. ;
  366. ;     Returns
  367. ;    ah = printer status
  368. ;         01h, printer busy
  369. ;         90h, printer ready
  370. ;
  371. ;
  372. ;   Extended int 17 Calls
  373. ;   ---------------------
  374. ;
  375. ;   Get Control Block Address
  376. ;    ah = 0c0h, funtion code
  377. ;    dx = printer number
  378. ;
  379. ;     Returns
  380. ;    es:bx -> control block
  381. ;
  382. ;
  383. ;   Build Control Record (Flushes pending writes)
  384. ;    ah = 0c1h, funtion code
  385. ;    dx = printer number
  386. ;    ds:si -> ASCIIZ string to save for display
  387. ;
  388. ;
  389. ;   Flush Pending Writes
  390. ;    ah = 0c2h, funtion code
  391. ;    dx = printer number
  392. ;
  393. ;
  394. ;   Cancel Printer Queue (Flush all queued output)
  395. ;    ah = 0c3h, funtion code
  396. ;    dx = printer number
  397. ;
  398. ;
  399. ;   Query Spooler Active
  400. ;    ah = 0c4h, function code
  401. ;
  402. ;     Returns
  403. ;    di = 0b0bfh
  404. ;    si = segment
  405. ;
  406. ;
  407. ;   Job Skip Printer Queue (Cancels upto the pause record)
  408. ;    ah = 0c5h, funtion code
  409. ;    dx = printer number
  410. ;
  411. ;
  412. ;   Check Printer Queue Status
  413. ;    ah = 0c6h, funtion code
  414. ;    dx = printer number
  415. ;
  416. ;     Returns
  417. ;    ax = 0 = printer not active or at pause
  418. ;         1 = printer busy
  419. ;
  420. ;
  421. ;   Close Queue
  422. ;    ah = 0c7h, funtion code
  423. ;    dx = printer number (0)
  424. ;
  425. ;
  426. ; ----------------------------------------------------------------------
  427.  
  428. int17tbl    label   byte            ; funtion code/routine table
  429.         db        00h             ; print a character
  430.         dw        i17h40
  431.         db        01h             ; initialize printer
  432.         dw        i17h45
  433.         db        02h             ; get printer status
  434.         dw        i17h50
  435.         db        0c0h            ; get control block addr
  436.         dw        i17h55
  437.         db        0c1h            ; build control record
  438.         dw        i17h60
  439.         db        0c2h            ; flush pending writes
  440.         dw        i17h65
  441.         db        0c3h            ; cancel printer queue
  442.         dw        i17h70
  443.         db        0c5h            ; job skip queue
  444.         dw        i17h80
  445.         db        0c6h            ; check printer status
  446.         dw        i17h85
  447.         db        0c7h            ; set close queue flag
  448.         dw        i17h89
  449.         db        0ffh            ; end of list marker
  450.         dw        i17h50            ; ..default get printer stat
  451.  
  452. i17hcnt     db        0                ; int 17 counter
  453.  
  454. int17        proc                ; spool to printer interrupt
  455.         cmp     ah, 0c4h            ; q. spooler active request?
  456.         jne     i17h05            ; a. no .. continue
  457.  
  458.         mov     di, 0b0bfh            ; move in signature bytes
  459.         mov     si, cs            ; ..and our segment
  460.         iret                ; ..return to caller
  461.  
  462. i17h05:     push    ds                ; save registers
  463.         push    bx                ;
  464.  
  465.         push    cs                ; get addressibility
  466.         pop     ds                ; . . . .
  467.  
  468.         mov     bx, offset qhlpt1        ; bx -> lpt1 control block
  469.  
  470. i17h10:     cmp     dx, [bx].lpprtnbr        ; q. right printer?
  471.         je        i17h15            ; a. yes .. check if spool'g
  472.  
  473.         add     bx, lplen            ; bx -> next printer block
  474.  
  475.         cmp     bx, offset qhlpt3        ; q. last printer?
  476.         jna     i17h10            ; a. no .. try again
  477.         jmp     short i17h19        ; else .. old int 17 rtn
  478.  
  479.  
  480. i17h15:     cmp     [bx].lpstatus, 0feh     ; q. being spooled?
  481.         jb        i17h20            ; a. yes .. continue
  482.  
  483. i17h19:     cmp     ah, 0c0h            ; q. one of ours?
  484.         jb        i17h19b            ; a. no .. goto old int 17
  485.  
  486.         cmp     ah, 0c7h            ; q. still one of ours?
  487.         ja        i17h19b            ; a. no .. goto old int 17
  488.  
  489.         cmp     ah, 0c0h            ; q. get printer ctrl blk?
  490.         je        i17h20            ; a. yes .. continue
  491.  
  492.         cmp     ah, 0c6h            ; q. check printer?
  493.         jne     i17h19a            ; a. no .. return to caller
  494.  
  495.         xor     ax, ax            ; ax = 0, printer not busy
  496.  
  497. i17h19a:    pop     bx                ; restore registers
  498.         pop     ds                ;
  499.         iret                ; ..then return to caller
  500.  
  501. i17h19b:    pop     bx                ; restore registers
  502.         pop     ds                ;
  503.         jmp     cs:oldint17         ; then goto old int 17 rtn
  504.                         ; ..and rtn directly to user
  505.  
  506. i17h20:     mov     i17hcnt, 1            ; show int 17 called
  507.         push    ax                ; save rest of registers
  508.         push    si                ;
  509.         mov     si, offset int17tbl     ; si -> function/routine tbl
  510.  
  511. i17h25:     cmp     ah, byte ptr [si]        ; q. do we point at this fnc
  512.         je        i17h30            ; a. yes .. exit loop
  513.  
  514.         add     si, 3            ; si -> next list entry
  515.  
  516.         cmp     byte ptr [si], 0ffh     ; q. end of the list?
  517.         jne     i17h25            ; a. no .. loop some more
  518.  
  519. i17h30:     jmp     word ptr 1[si]        ; ..goto the proper routine
  520.  
  521.  
  522.                         ; --------------------------
  523. i17h40        label   byte            ; print a character
  524.         cmp     [bx].lpwrite, 1        ; q. write needed?
  525.         jne     i17h401            ; a. no .. go ahead
  526.  
  527.         call    writeqr            ; q. able to send qr away?
  528.         jnc     i17h401            ; a. yes .. continue
  529.         jmp     i17h90            ; else .. return w/busy
  530.  
  531. i17h401:    cli                 ; stop ints for a second
  532.         add     word ptr [bx].lpinque, 1    ; increment total in que
  533.         adc     word ptr [bx].lpinque+2, 0
  534.         sti                 ; restart ints
  535.  
  536.         push    bx                ; save printer ctrl blk ptr
  537.         mov     si, [bx].lpwrkqr        ; si -> work queue record
  538.         inc     i17hflg            ; critical process active
  539.         mov     bx, [si].qrbytes        ; bx = nbr of bytes in rec
  540.         mov     [si+bx].qrdata, al        ; store character in que rec
  541.         inc     [si].qrbytes        ; bump character counter
  542.         dec     i17hflg            ; critical process now done
  543.         pop     bx                ; restore register
  544.  
  545.         cmp     [si].qrbytes, qrdlen    ; q. queue record filled?
  546.         je        i17h402            ; a. yes .. move it
  547.         jmp     i17h90            ; else, just return
  548.  
  549. i17h402:    mov     [si].qrcontrol, 0        ; make record type be data
  550.  
  551.         call    writeqr            ; q. able to send rec away?
  552.         jc        i17h403            ; a. no .. setup for later
  553.         jmp     i17h90            ; else .. rtn to caller ok
  554.  
  555. i17h403:    mov     [bx].lpspools, 90h        ; show printer still ready
  556.         mov     [bx].lpwrite, 1        ; ..and write needed
  557.         jmp     i17h90            ; ..then return to caller
  558.  
  559.  
  560.                         ; --------------------------
  561. i17h45        label   byte            ; initialize printer
  562.         mov     si, [bx].lpwrkqr        ; si -> work queue record
  563.         mov     [si].qrcontrol, 1        ; show next record is ctrl
  564.         call    writeqr            ; send queue record away
  565.  
  566.         mov     [si].qrcontrol, 4        ; record is an init. record
  567.         call    writeqr            ; send queue record away
  568.         jmp     i17h90            ; ..then return to caller
  569.  
  570.  
  571.                         ; --------------------------
  572. i17h50        label   byte            ; get printer status
  573.         mov     si, [bx].lpwrkqr        ; si -> work queue record
  574.         jmp     i17h90            ; ..in exit routine
  575.  
  576.  
  577.                         ; --------------------------
  578. i17h55        label   byte            ; get printer control block
  579.         mov     ax, ds            ; ax = our data segment
  580.         mov     es, ax            ; es -> our data segment
  581.  
  582.         pop     si                ; restore registers
  583.         pop     ax                ;
  584.         add     sp, 2            ; skip restoring es:bx
  585.         pop     ds                ;
  586.         iret                ; return, es:bx -> prt blk
  587.  
  588.  
  589.                         ; --------------------------
  590. i17h60        label   byte            ; build control record
  591.         push    bp                ; save bp register
  592.         mov     bp, sp            ; get stack base pointer
  593.  
  594.         push    di                ; save some more registers
  595.         push    si
  596.         push    cx
  597.         push    es
  598.         push    ds
  599.  
  600.         mov     si, [bx].lpwrkqr        ; si -> work queue record
  601.         push    si                ; save record pointer
  602.         mov     [si].qrcontrol, 1        ; show next record is ctrl
  603.         call    writeqr            ; send queue record away
  604.  
  605.         mov     [si].qrcontrol, 2        ; rec type = pause w/msg
  606.         lea     di, [si].qrdata + 2     ; di -> string area
  607.  
  608.         push    ds                ; make ..
  609.         pop     es                ; es -> our segment
  610.  
  611.         mov     si, [bp]+2            ; si -> user's string
  612.         mov     ds, [bp]+8            ; ds:si -> asciiz string
  613.         xor     cx, cx            ; cx = character counter
  614.  
  615. i17h62:     cmp     cx, qrdlen-3        ; q. enough moved?
  616.         jb        i17h63            ; a. yes .. move no more
  617.  
  618.         xor     al, al            ; simulate end
  619.         jmp     short i17h64        ; .. store a null
  620.  
  621. i17h63:     lodsb                ; al = character from user
  622. i17h64:     stosb                ; ..store in the queue rec
  623.         inc     cx                ; cx = copied count
  624.  
  625.         or        al, al            ; q. last character?
  626.         jnz     i17h62            ; a. no .. loop
  627.  
  628.         cmp     cx, 35            ; q. will message scroll?
  629.         jna     i17h64a            ; a. no .. skip extra char
  630.  
  631.         mov     byte ptr es:[di-1],''  ; save end of message char
  632.         inc     cx                ; .. and count in length
  633.  
  634. i17h64a:    inc     cx                ; cx = nbr chars + length
  635.         pop     si                ; si -> queue record
  636.         pop     ds                ; restore our data segment
  637.         mov     [si].qrbytes, cx        ; save byte count
  638.         sub     cx, 2            ; cx = string length
  639.         mov     word ptr [si].qrdata, cx    ; save in message
  640.  
  641.         pop     es                ; restore rest of the regs
  642.         pop     cx                ;
  643.         pop     si                ;
  644.         pop     di                ;
  645.         pop     bp                ;
  646.  
  647.         call    writeqr            ; send queue record away
  648.         jmp     short i17h90        ; ..then exit routine
  649.  
  650.  
  651.                         ; --------------------------
  652. i17h65        label   byte            ; flush pending writes
  653.         mov     bx, offset qhlpt1        ; bx -> lpt1 control block
  654.  
  655. i17h67:     mov     si, [bx].lpwrkqr        ; si -> work queue record
  656.  
  657.         cmp     [si].qrbytes, 0        ; q. anything there?
  658.         jne     i17h68            ; a. no .. next printer
  659.  
  660.         call    writeqr            ; else .. send queue record
  661.  
  662. i17h68:     add     bx, lplen            ; bx -> next printer block
  663.  
  664.         cmp     bx, offset qhlpt3        ; q. last printer?
  665.         jna     i17h67            ; a. no .. try again
  666.  
  667.         jmp     short i17h90        ; ..in exit routine
  668.  
  669.  
  670.                         ; --------------------------
  671. i17h70        label   byte            ; cancel printer queue
  672.         mov     [bx].lpflush, 1        ; show queue needs clearing
  673.         jmp     short i17h82        ; ..and jump into common code
  674.  
  675.  
  676.                         ; --------------------------
  677. i17h80        label   byte            ; job skip printer queue
  678.         mov     [bx].lpflush, 2        ; show which queue to skip
  679.  
  680. i17h82:     mov     si, [bx].lpwrkqr        ; si -> work queue record
  681.         mov     ax, [si].qrbytes        ; ax = what was in qrec
  682.         cli                 ; stop ints for a second
  683.         sub     word ptr [bx].lpinque, ax    ; decrement total in que
  684.         sbb     word ptr [bx].lpinque+2, 0    ; . . .
  685.         sti                 ; restart ints
  686.  
  687.         mov     [si].qrbytes, 0        ; clear queue record
  688.         mov     [si].qrcontrol, 0        ; ..and control byte
  689.         mov     [bx].lpstatus, 5        ; show waiting flush
  690.  
  691.                         ; --------------------------
  692. i17h90:     pop     si                ; normal calls return
  693.         pop     ax                ;
  694.         mov     ah, [bx].lpspools        ; return spool status
  695.         pop     bx                ;
  696.         pop     ds                ;
  697.         iret                ; return to caller
  698.  
  699.  
  700.                         ; --------------------------
  701. i17h85        label   byte            ; check printer queue status
  702.         push    cx                ; save another register
  703.         xor     ax, ax            ; ax = 0, for quiesced prt
  704.  
  705.         cmp     [bx].lpstatus, 2        ; q. waiting for pause?
  706.         jae     i17h86            ; a. yes .. ready
  707.  
  708.         cmp     [bx].lpfirstqr, 0        ; q. queue empty?
  709.         jnz     i17h87            ; a. no .. return now
  710.  
  711. i17h86:     mov     cx, [bx].lpdqin        ; cx -> nxt input going byte
  712.  
  713.         cmp     cx, [bx].lpdqout        ; q. empty printer puddle?
  714.         je        i17h88            ; a. yes ..
  715.  
  716. i17h87:     inc     ax                ; ax = 1, printer busy
  717.  
  718. i17h88:     pop     cx                ; return to caller
  719.         pop     si                ;
  720.         add     sp, 2            ;
  721.         pop     bx                ;
  722.         pop     ds                ;
  723.         iret                ; return to caller
  724.  
  725.  
  726.  
  727.                         ; --------------------------
  728. i17h89        label   byte            ; set close print queue flag
  729.         mov     close_req, 1        ; setup close request flag
  730.         jmp     short i17h90        ; ..then exit
  731.  
  732.  
  733.  
  734. int17        endp
  735.  
  736.  
  737. ; ----------------------------------------------------------------------
  738. ;   This routine will try to copy the queue data to the printer's memory
  739. ;   puddle, but if that is not ready, the data will be written to the
  740. ;   spool.
  741. ;
  742. ;   Entry
  743. ;    bx -> printer control block
  744. ;
  745. ;   Returns
  746. ;    carry = DOS busy, try later
  747. ;
  748. ; ----------------------------------------------------------------------
  749.  
  750. writeqr     proc
  751.         push    si                ; save registers
  752.         push    dx                ;
  753.  
  754.         mov     si, [bx].lpwrkqr        ; si -> queue record
  755.  
  756.         cmp     [si].qrcontrol, 1        ; q. control record?
  757.         ja        writeqr10            ; a. yes .. write it out
  758.  
  759.         cmp     [bx].lpcontrol, 0        ; q. draining printer?
  760.         jne     writeqr10            ; a. yes .. write to queue
  761.  
  762.         call    movetoprt            ; move spool record to prt
  763.         jnc     writeqr30            ; ..everything ok, continue
  764.  
  765. writeqr10:  call    usingdos            ; q. DOS available?
  766.         jc        writeqr25            ; a. no .. try later
  767.  
  768. writeqr20:  mov     dx, si            ; dx -> work queue record
  769.         mov     ax, [bx].lpprtnbr        ; ax = printer number
  770.         mov     ah, 1            ; ah = write queue record
  771.  
  772.         call    qh                ; q. write the record ok?
  773.         jnc     writeqr30            ; a. yes .. continue
  774.  
  775. writeqr25:  mov     [bx].lpspools, 01h        ; show printer busy
  776.         mov     [bx].lpwrite, 1        ; show a write is needed
  777.         pop     dx                ; restore registers
  778.         pop     si                ;
  779.         stc                 ; set carry flag (problems)
  780.         ret                 ; ..and return to caller
  781.  
  782. writeqr30:  mov     [si].qrcontrol, 0        ; clear control byte
  783.         mov     [si].qrbytes, 0        ; ..character count
  784.         mov     [bx].lpwrite, 0        ; ..write needed flag
  785.         mov     [bx].lpspools, 90h        ; show printer ready, again
  786.  
  787.         pop     dx                ; restore registers
  788.         pop     si                ;
  789.         clc                 ; clear carry flag
  790.         ret                 ; ..and rtn to caller, "ok"
  791.  
  792. writeqr     endp
  793.  
  794.  
  795. ; ----------------------------------------------------------------------
  796. ;   This routine will try to move the current queue record to the
  797. ;   de-queuing routines memory puddle.    This helps cut down on spool
  798. ;   i/o when the printer is keeping up with the application program.
  799. ;
  800. ;   Entry
  801. ;    bx -> printer control block
  802. ;
  803. ;   Returns
  804. ;    carry = printer buffer not empty enough
  805. ;
  806. ; ----------------------------------------------------------------------
  807.  
  808. movetoprt   proc
  809.         cmp     [bx].lpfirstqr, 0        ; q. any records in chain?
  810.         jne     movetop10            ; a. yes .. move to spool
  811.  
  812.         push    si                ; save register
  813.         push    cx
  814.  
  815.         call    getprt            ; get available prt space
  816.  
  817.         mov     si, [bx].lpwrkqr        ; si -> work queue record
  818.  
  819.         cmp     [si].qrbytes, cx        ; q. enough room in memory?
  820.         jbe     movetop20            ; a. yes .. move in the rec
  821.  
  822.         pop     cx                ; restore register
  823.         pop     si                ;
  824. movetop10:  stc                 ; ..set carry flag
  825.         ret                 ; ..then rtn w/can't do stat
  826.  
  827. movetop20:  call    moverecord            ; move record to puddle
  828.  
  829.         pop     cx                ; restore regs
  830.         pop     si
  831.         clc                 ; clear carry .. show "ok"
  832.         ret                 ; ..then return to caller
  833.  
  834. movetoprt   endp
  835.  
  836.  
  837. ; ----------------------------------------------------------------------
  838. ;   This routine will return the availability of DOS for file i/o.
  839. ;   If the spool is not using DOS file i/o (using conventional or EMS
  840. ;   memory) then the return will always be "ok"
  841. ;
  842. ;   Returns
  843. ;    carry = DOS not ready for file i/o
  844. ;
  845. ; ----------------------------------------------------------------------
  846.  
  847. usingdos    proc
  848.  
  849.         test    pcsflg, pcsflgdk        ; q. are we using disk queue
  850.         jz        usingdos90            ; a. no .. return ok
  851.  
  852.         cmp     i28hflag, 0         ; q. called from DOS Waiting
  853.         jne     usingdos90            ; a. yes .. then we're ok
  854.  
  855.         cmp     intsbusy, 0         ; q. one of interrupts busy?
  856.         jne     usingdos80            ; a. yes .. exit w/wait code
  857.  
  858.         push    es                ; save registers
  859.         push    bx                ; ..
  860.         les     bx, dos_busy        ; es:bx -> DOS busy flag
  861.  
  862.         cmp     byte ptr es:[bx], 0     ; q. DOS busy
  863.         pop     bx                ; ...restore registers
  864.         pop     es                ; ...
  865.         je        usingdos90            ; a. no .. return to user
  866.  
  867. usingdos80: stc                 ; else .. show DOS is busy
  868.         ret                 ; ..and return to caller
  869.  
  870. usingdos90: clc                 ; clear carry .. a-ok
  871.         ret                 ; ..and return to caller
  872.  
  873. usingdos    endp
  874.  
  875.  
  876.  
  877. ; ----------------------------------------------------------------------
  878. ;   This routine moves a record to the printer memory puddle and
  879. ;   adjusts the pointers.
  880. ;
  881. ;   Entry
  882. ;    bx -> printer control block
  883. ;    si -> queue record to move
  884. ;
  885. ; ----------------------------------------------------------------------
  886.  
  887. moverecord  proc
  888.         push    ax                ; save registers
  889.         push    cx
  890.         push    di
  891.         push    si
  892.         push    es
  893.         cld                 ; clear direction flag
  894.  
  895.         mov     di, [bx].lpdqin        ; di -> input offset
  896.         mov     es, word ptr [bx].lpdqbuf+2 ; es = segment of puddle
  897.         mov     cx, lpt_dqsz        ; cx -> end of puddle
  898.         sub     cx, di            ; cx = max chars in 1st copy
  899.  
  900.         mov     al, [si].qrcontrol        ; al = record type
  901.         mov     [bx].lpcontrol, al        ; save record type
  902.  
  903.         cmp     al, 1            ; q. end of data record?
  904.         jne     moverec20            ; a. no .. continue
  905.  
  906.         xor     al, al            ; al = new record type
  907.  
  908. moverec20:  mov     [bx].lpstatus, al        ; setup new status
  909.  
  910.         mov     ax, [si].qrbytes        ; ax = nbr of chars in rec
  911.         mov     [si].qrbytes, 0        ; then clear count
  912.         mov     [si].qrcontrol,0        ; ..and record type
  913.         lea     si, [si].qrdata        ; si -> queue record data
  914.  
  915.         or        ax, ax            ; q. anything to move?
  916.         jz        moverec90            ; a. no .. better exit
  917.  
  918.         cmp     ax, cx            ; q. enough room?
  919.         ja        moverec30            ; a. no .. need two..
  920.  
  921.         mov     cx, ax            ; cx = amount to move
  922.         jmp     short moverec40        ; goto common code
  923.  
  924. moverec30:  push    cx                ; save 1st portion length
  925.  
  926.        rep  movsb                ; move 1st portion of data
  927.  
  928.         pop     cx                ; cx = 1st portion length
  929.         sub     ax, cx            ; ax = get new length ..
  930.         mov     cx, ax            ; cx = setup for rep/movsb
  931.         xor     di, di            ; di -> start of puddle
  932.  
  933. moverec40:
  934.        rep  movsb                ; move queue data to puddle
  935.  
  936.         cmp     [bx].lpstatus, 2        ; q. pause message?
  937.         je        moverec90            ; a. yes .. data doesn't count
  938.  
  939.         cmp     di, lpt_dqsz        ; q. at end of puddle?
  940.         jb        moverec50            ; a. no .. save as is
  941.  
  942.         xor     di, di            ; di -> first byte of pud'l
  943.  
  944. moverec50:  mov     [bx].lpdqin, di        ; store new end pointer
  945.  
  946. moverec90:  pop     es                ; restore registers
  947.         pop     si
  948.         pop     di
  949.         pop     cx
  950.         pop     ax
  951.         ret                 ; ..then return to caller
  952.  
  953. moverecord  endp
  954.  
  955.  
  956. ; ----------------------------------------------------------------------
  957. ;   This routine interrupts writes using function 40h to any printer
  958. ;   handles.  Writes are parcel'd out to the max of the requested write
  959. ;   or the remainding size of the spooler's available buffer.  If the
  960. ;   handle did use the spooler than the remainder of the write request
  961. ;   is send in chunks.    This will allow the disk write routines enough
  962. ;   of a breath to write their buffers to disk.
  963. ;
  964. ; ----------------------------------------------------------------------
  965.  
  966. int21        proc                ; interrupt 21 handler
  967.         cmp     cs:i21hflg, 0        ; q. int 21 in use?
  968.         jz        i21h10            ; a. no .. check it out
  969.         jmp     cs:oldint21         ; ..then goto DOS
  970.  
  971. i21h10:     inc     cs:i21hflg            ; mark that we are here
  972.  
  973.         call    chkprt            ; check for any reads/writes
  974.  
  975.         cmp     ah, 40h            ; q. write to file handle?
  976.         je        i21h15            ; a. yes .. check it out
  977.         jmp     short i21h18        ; else .. goto DOS
  978.  
  979. i21h15:     push    dx                ; save buffer address
  980.         push    ax                ; .. and function
  981.  
  982.         mov     ax, 4400h            ; ax = IOCTL/Get Dev Info
  983.         pushf                ; emulate an interrupt
  984.         call    cs:oldint21         ; call DOS
  985.  
  986.         cmp     al, 0c0h            ; q. printer, maybe?
  987.         pop     ax                ; .. restore ax
  988.         pop     dx                ; .. restore dx
  989.         je        i21h20            ; a. yes .. continue
  990.  
  991. i21h18:     dec     cs:i21hflg            ; clear in use flag
  992.         jmp     cs:oldint21         ; ..then goto DOS
  993.  
  994. i21h20:     push    bx                ; save registers
  995.         push    dx
  996.         push    cx
  997.  
  998.         mov     cs:i17hcnt, 0        ; clear int 17 used flag
  999.         mov     cs:i21hufh, bx        ; save file handle
  1000.         mov     cs:i21hlen, cx        ; ..remaining length
  1001.  
  1002. i21h30:     cli                 ; disallow ints
  1003.         call    chkprt            ; write any full buffers
  1004.         call    getmax            ; find max writable length
  1005.  
  1006.         sti                 ; let int's happen
  1007.         or        bx, bx            ; q. anyplace to write yet?
  1008.         jz        i21h30            ; a. no .. try again
  1009.  
  1010.         cli
  1011.         cmp     bx, cx            ; q. plenty of room?
  1012.         jae     i21h40            ; a. yes .. continue
  1013.  
  1014.         mov     cx, bx            ; cx = length to write
  1015.  
  1016. i21h40:     mov     ah, 40h            ; ah = write to handle
  1017.         mov     bx, cs:i21hufh        ; bx = original handle
  1018.         pushf                ; emulate an interrupt
  1019.         call    cs:oldint21         ; q. error writing?
  1020.         jnc     i21h50            ; a. no .. continue
  1021.  
  1022.         pop     cx                ; restore register
  1023.         jmp     short i21h70        ; ..and exit thru bottom
  1024.  
  1025. i21h50:     mov     cx, cs:i21hlen        ; cx = what was left
  1026.  
  1027.         or        ax, ax            ; q. anything print?
  1028.         jz        i21h60            ; a. no .. better exit
  1029.  
  1030.         sub     cx, ax            ; q. anything left?
  1031.         jz        i21h60            ; a. no .. exit loop
  1032.  
  1033.         mov     cs:i21hlen, cx        ; save remaining length
  1034.         add     dx, ax            ; dx -> next block full
  1035.  
  1036.         cmp     cs:i17hcnt, 0        ; q. int 17 used?
  1037.         jne     i21h30            ; a. yes .. loop back
  1038.         jmp     i21h40            ; else .. finish up
  1039.  
  1040. i21h60:     pop     cx                ; restore registers
  1041.         mov     ax, cx            ; ax = written count
  1042.         clc                 ; show everything ok..
  1043.  
  1044. i21h70:     pop     dx                ; restore the rest
  1045.         pop     bx
  1046.         dec     cs:i21hflg            ; clear int 21 use flag
  1047.         iret                ; ..and return to caller
  1048.  
  1049. int21        endp
  1050.  
  1051.  
  1052.  
  1053. ; ----------------------------------------------------------------------
  1054. ;   This routine checks for queue reads/writes which need to be done,
  1055. ;   then calls the queue management routines to handle them.
  1056. ;
  1057. ; ----------------------------------------------------------------------
  1058.  
  1059. chkprt        proc
  1060.         cmp     cs:chkprtflg, 0        ; q. chkprt in use?
  1061.         jne     chkprt0            ; a. yes.. exit
  1062.  
  1063.         cmp     cs:qinuseflg, 0        ; q. qh in use?
  1064.         je        chkprt00            ; a. no .. continue
  1065.  
  1066. chkprt0:    ret                 ; else .. return to caller
  1067.  
  1068. chkprt00:   inc     cs:chkprtflg        ; show we're in routine
  1069.  
  1070.         push    ax                ; save registers
  1071.         push    bx
  1072.         push    cx
  1073.         push    dx
  1074.         push    si
  1075.         push    ds
  1076.  
  1077.         push    cs                ; get addressibility
  1078.         pop     ds                ; . . . .
  1079.  
  1080.         cmp     close_req, 1        ; q. need to close queue?
  1081.         jne     chkprt05            ; a. no .. continue
  1082.  
  1083.         mov     ax, 7            ; ax = close spool function
  1084.         call    dword ptr qrtn        ; call queue routine
  1085.         mov     close_req, 2        ; show queue closed
  1086.         jmp     chkprt90            ; ..then return to caller
  1087.  
  1088. chkprt05:   mov     bx, offset qhlpt1        ; bx -> lpt1 control block
  1089.  
  1090. chkprt10:   cmp     [bx].lpflush, 0        ; q. need to flush queue?
  1091.         je        chkprt40            ; a. no .. continue
  1092.  
  1093.         mov     cl, [bx].lpflush        ; cl = flush mode
  1094.         mov     dx, dqrec            ; dx -> deque record
  1095.         mov     si, dx            ; si -> same record
  1096.  
  1097. chkprt20:   mov     ax, [bx].lpprtnbr        ; ah = read, al = prt nbr
  1098.  
  1099.         call    qh                ; q. get the next qrecord?
  1100.         jc        chkprt30            ; a. no .. try later
  1101.  
  1102.         cmp     [si].qrcontrol, 1        ; q. data record?
  1103.         ja        chkprt25            ; a. no .. continue
  1104.  
  1105.         mov     ax, [si].qrbytes        ; ax = nbr bytes in record
  1106.         sub     word ptr [bx].lpinque, ax    ; subtract out this rec
  1107.         sbb     word ptr [bx].lpinque+2, 0    ; . . .
  1108.  
  1109. chkprt25:   cmp     cl, 1            ; q. flush entire queue
  1110.         je        chkprt20            ; a. yes .. get next qrec
  1111.  
  1112.         cmp     [si].qrcontrol, 1        ; q. next rec a pause?
  1113.         jne     chkprt20            ; a. no .. get next qrec
  1114.  
  1115. chkprt30:   mov     [bx].lpflush, 0        ; clear flush flag
  1116.         mov     [bx].lpcontrol, 0        ; ..and last control rec
  1117.  
  1118. chkprt40:   cmp     [bx].lpwrite, 1        ; q. write needed?
  1119.         jne     chkprt42            ; a. no .. check for reads
  1120.  
  1121.         call    writeqr            ; send qr away..
  1122.  
  1123. chkprt42:   cmp     [bx].lpstatus, 1        ; q. printer printing?
  1124.         ja        chkprt70            ; a. no .. next!
  1125.  
  1126.         call    getprt            ; ax = max puddle space
  1127.  
  1128.         cmp     [bx].lpcontrol, 0        ; q. next qrec data?
  1129.         je        chkprt45            ; a. yes .. get it
  1130.  
  1131.         cmp     cx, lpt_dqsz - 1        ; q. buffer empty?
  1132.         jne     chkprt70            ; a. no .. wait till done
  1133.  
  1134.         xor     ax, ax            ; ax = 0, for convience
  1135.         mov     [bx].lpdqin, ax        ; clear input
  1136.         mov     [bx].lpdqout, ax        ; ..and output pointers
  1137.  
  1138. chkprt45:   cmp     cx, qrlen            ; q. enough for a read?
  1139.         jb        chkprt70            ; a. no .. next printer
  1140.  
  1141.         mov     ax, [bx].lpprtnbr        ; ah = read, al = prt nbr
  1142.         mov     dx, dqrec            ; dx -> deque record
  1143.         call    qh                ; q. get the next qrecord?
  1144.         jc        chkprt50            ; a. no .. try later
  1145.  
  1146.         mov     si, dx            ; si -> record
  1147.         jmp     short chkprt60        ; ..continue w/common code
  1148.  
  1149. chkprt50:   mov     si, [bx].lpwrkqr        ; si -> in core wrk record
  1150.  
  1151.         cmp     i17hflg, 0            ; q. anybody do'in int 17?
  1152.         jnz     chkprt70            ; a. yes .. wait till later
  1153.  
  1154. chkprt60:   call    moverecord            ; move record to prt puddle
  1155.  
  1156. chkprt70:   add     bx, lplen            ; bx -> next printer block
  1157.  
  1158.         cmp     bx, offset qhlpt3        ; q. last printer?
  1159.         ja        chkprt90            ; a. yes .. return to caller
  1160.         jmp     chkprt10            ; else .. next printer
  1161.  
  1162. chkprt90:   pop     ds                ; restore register
  1163.         pop     si
  1164.         pop     dx
  1165.         pop     cx
  1166.         pop     bx
  1167.         pop     ax
  1168.         dec     cs:chkprtflg        ; clear rtn in use flag
  1169.         ret                 ; ..and return to caller
  1170.  
  1171. chkprt        endp
  1172.  
  1173.  
  1174.  
  1175. ; ----------------------------------------------------------------------
  1176. ;   The routine returns the maximun write which the spooling system can
  1177. ;   withstand by DOS at this moment.  This is acually the smallest
  1178. ;   available buffer space.
  1179. ;
  1180. ;   Returns
  1181. ;    bx = maximum available space in work qrecords
  1182. ;
  1183. ; ----------------------------------------------------------------------
  1184.  
  1185. getmax        proc
  1186.         push    ax                ; save registers
  1187.         push    cx
  1188.         push    si
  1189.         push    ds
  1190.  
  1191.         push    cs                ; get addressibility
  1192.         pop     ds
  1193.  
  1194.         mov     ax, qrdlen            ; ax = max value
  1195.         mov     bx, offset qhlpt1        ; bx -> lpt1 control block
  1196.  
  1197. getmax10:   mov     si, [bx].lpwrkqr        ; si -> int 17's queue rec
  1198.  
  1199.         mov     cx, qrdlen            ; cx = max record size
  1200.         sub     cx, [si].qrbytes        ; cx = remaining free bytes
  1201.  
  1202.         cmp     ax, cx            ; q. this printer less?
  1203.         jb        getmax20            ; a. no .. get next printer
  1204.  
  1205.         mov     ax, cx            ; ax = new lesser value
  1206.  
  1207. getmax20:   add     bx, lplen            ; bx -> next printer block
  1208.  
  1209.         cmp     bx, offset qhlpt3        ; q. last printer?
  1210.         jna     getmax10            ; a. no .. try again
  1211.  
  1212.         mov     bx, ax            ; bx = return value
  1213.         pop     ds                ; restore registers
  1214.         pop     si                ;
  1215.         pop     cx                ;
  1216.         pop     ax                ;
  1217.         ret                 ; return to caller
  1218.  
  1219. getmax        endp
  1220.  
  1221.  
  1222.  
  1223. ; ----------------------------------------------------------------------
  1224. ;   The routine returns the available area in this printer's memory
  1225. ;   puddle.
  1226. ;
  1227. ;   Entry
  1228. ;    bx -> printer control block
  1229. ;
  1230. ;   Returns
  1231. ;    cx = maximum available space in memory puddle
  1232. ;
  1233. ; ----------------------------------------------------------------------
  1234.  
  1235. getprt        proc
  1236.         push    ax                ; save registers
  1237.  
  1238.         mov     ax, [bx].lpdqin        ; ax -> next incoming char
  1239.         mov     cx, [bx].lpdqout        ; cx -> next printed char
  1240.  
  1241.         cmp     ax, cx            ; q. buffer empty?
  1242.         je        getprt10            ; a. yes .. use max
  1243.         jb        getprt20            ; almost full .. any left?
  1244.  
  1245.         sub     ax, cx            ; ax = what's used
  1246.         mov     cx, (lpt_dqsz - 1)        ; cx = max available
  1247.         sub     cx, ax            ; cx = what's not used
  1248.         pop     ax                ; restore register
  1249.         ret                 ; ..and return to caller
  1250.  
  1251. getprt10:   mov     cx, (lpt_dqsz - 1)        ; cx = puddle size
  1252.         pop     ax                ; restore register
  1253.         ret                 ; ..and return to caller
  1254.  
  1255. getprt20:   sub     cx, ax            ; cx = what's left
  1256.         dec     cx                ; ..minus 1
  1257.         pop     ax                ; restore register
  1258.         ret                 ; ..and return to caller
  1259.  
  1260. getprt        endp
  1261.  
  1262.  
  1263. ; ----------------------------------------------------------------------
  1264. ;   This routine checks the printers memory puddles for needing
  1265. ;   refilling, and checks the "write needed" flags on the spooling side.
  1266. ; ----------------------------------------------------------------------
  1267.  
  1268. int28        proc
  1269.         sti                 ; enable interrupts
  1270.         inc     cs:i28hflag         ; show we're in int 28 rtn
  1271.  
  1272.         call    chkprt            ; handle printer puddles
  1273.  
  1274.         dec     cs:i28hflag         ; show we're out of int 28
  1275.         jmp     dword ptr cs:oldint28   ; jump to old int 28 routine
  1276.  
  1277. int28        endp
  1278.  
  1279.  
  1280.  
  1281. ; ----------------------------------------------------------------------
  1282. ;   This routine keeps track of the int 13 usage for the DOS-in-use rtn.
  1283. ; ----------------------------------------------------------------------
  1284.  
  1285. int13        proc
  1286.         sti                 ; enable interrupts
  1287.         inc     cs:intsbusy         ; show interrupt is busy
  1288.  
  1289.         pushf                ; fake interrupt call
  1290.         call    dword ptr cs:oldint13   ; call the old int 13 rtn
  1291.  
  1292.         dec     cs:intsbusy         ; now show we're done
  1293.         iret                ; ..and return to caller
  1294. int13        endp
  1295.  
  1296.  
  1297.  
  1298. ; ----------------------------------------------------------------------
  1299. ;   Try to print some characters at timer tick.
  1300. ; ----------------------------------------------------------------------
  1301.  
  1302. int08        proc
  1303.         push    ds                ; save registers
  1304.  
  1305.         push    cs                ; setup addressibility
  1306.         pop     ds
  1307.  
  1308.         pushf                ; fake interrupt call
  1309.         call    oldint08            ; call old timer stuff
  1310.  
  1311.         inc     i08hflg            ; lock out another call
  1312.                         ; ..and cnt if busy toprt'g
  1313.  
  1314.         cmp     i08hflg, 1            ; q. already doing stuff?
  1315.         jne     i08h10            ; a. yes .. exit quickly
  1316.  
  1317.         sti                 ; enable interrupts
  1318.         call    toprt            ; .. try to print
  1319.  
  1320.         call    tocps            ; tally cps speed
  1321.  
  1322.         call    usingdos            ; q. DOS available/needed?
  1323.         jc        i08h05            ; a. no .. try later
  1324.  
  1325.         call    chkprt            ; see if prt chars needed
  1326.  
  1327. i08h05:     mov     i08hflg, 0            ; clear flag for another call
  1328.  
  1329. i08h10:     inc     tsr_cnt            ; increment tsr counter
  1330.  
  1331.         cmp     tsr_req, 0            ; q. tsr wanted?
  1332.         jz        i08h30            ; a. no .. try later
  1333.  
  1334.         cmp     tsr_active, 0        ; q. tsr already active?
  1335.         jne     i08h30            ; a. yes .. continue
  1336.  
  1337.         cmp     chkprtflg, 0        ; q. check print active?
  1338.         jne     i08h30            ; a. yes .. continue
  1339.  
  1340.         inc     tsr_active            ; lock out another call
  1341.         sti                 ; enable interrupts
  1342.  
  1343.         call    usingdos            ; q. DOS available/needed?
  1344.         jc        i08h20            ; a. no .. try later
  1345.  
  1346.         call    tsr             ; do tsr stuff
  1347.  
  1348. i08h20:     mov     tsr_active, 0        ; clear tsr active flag
  1349.  
  1350. i08h30:     pop     ds                ; restore registers
  1351.         iret                ; ..and return to caller
  1352.  
  1353. int08        endp
  1354.  
  1355.  
  1356.  
  1357. ; ----------------------------------------------------------------------
  1358. ;   Intercept the hot key.
  1359. ; ----------------------------------------------------------------------
  1360.  
  1361. int09        proc
  1362.         cmp     cs:tsr_active, 0        ; q. tsr in use?
  1363.         jne     i09h15            ; a. yes .. get out quickly
  1364.  
  1365.         push    ax                ; save used register
  1366.         in        al, 60h            ; get key scan code
  1367.         cmp     al, cs:hot_key        ; q. our hot-key?
  1368.         je        i09h10            ; a. yes .. continue
  1369.  
  1370.         pop     ax                ; restore register
  1371.         jmp     short i09h15        ; ..and get out
  1372.  
  1373. i09h10:     mov     ah, 2            ; ah = get shift status fnc
  1374.         int     16h             ; call BIOS
  1375.  
  1376.         and     al, 0fh            ; al = 'shift' bits
  1377.  
  1378.         cmp     al, cs:shift_mask        ; q. match our combo?
  1379.         pop     ax                ; restore register
  1380.         je        i09h20            ; a. yes . continue
  1381. i09h15:     jmp     cs:oldint09         ; else .. do key as normal
  1382.  
  1383. i09h20:     pushf                ; call old int 9
  1384.         call    cs:oldint09         ; ..to finish reading key
  1385.  
  1386.         sti                 ; allow interrupts
  1387.         push    ax                ; save work register
  1388.  
  1389. i09h30:     mov     ah, 1            ; ah = get status
  1390.         int     16h             ; q. any keys availabl?
  1391.         jz        i09h40            ; a. no .. exit loop
  1392.  
  1393.         mov     ah, 0            ; ah = get key
  1394.         int     16h             ; get the key ..
  1395.         jmp     i09h30            ; ..and loop till kb empty
  1396.  
  1397. i09h40:     pop     ax                ; restore register
  1398.         inc     cs:tsr_req            ; show tsr request made
  1399.         iret                ; go back where we came from
  1400.  
  1401. int09        endp
  1402.  
  1403.  
  1404.  
  1405. ; ----------------------------------------------------------------------
  1406. ;   This routine keeps track of variables to calculate CPS during
  1407. ;   the TSR display routines.
  1408. ; ----------------------------------------------------------------------
  1409.  
  1410. tocps        proc
  1411.         push    ax                ; save registers
  1412.         push    bx
  1413.         push    dx
  1414.  
  1415.         lea     bx, qhlpt1            ; bx -> 1st printer block
  1416.  
  1417. tocps00:    cmp     [bx].lptmrflg, 0        ; q. update CPS?
  1418.         je        tocps90            ; a. no.. next printer
  1419.  
  1420. tocps10:    mov     ax, i08hflg         ; ax = nbr ticks this cycle
  1421.  
  1422.         add     word ptr [bx].lpticks, ax    ; accumulate total ticks
  1423.         adc     word ptr [bx].lpticks+2, 0    ; ..to print this much
  1424.  
  1425.         mov     [bx].lptmrflg, 0        ; reset timer flag
  1426.  
  1427. tocps90:    add     bx, lplen            ; bx -> next printer
  1428.  
  1429.         cmp     bx, offset qhlpt3        ; q. past last printer?
  1430.         jb        tocps00            ; a. no .. loop back
  1431.  
  1432.         pop     dx                ; restore registers
  1433.         pop     bx
  1434.         pop     ax
  1435.         ret                 ; ..and return to caller
  1436.  
  1437. tocps        endp
  1438.  
  1439.  
  1440.  
  1441. ; ----------------------------------------------------------------------
  1442. ;   This routine is the TSR user interface portion.  It displays current
  1443. ;   status, controls printers and printer queues.
  1444. ; ----------------------------------------------------------------------
  1445.  
  1446. tsr        proc
  1447.         push    ax                ; save registers
  1448.         push    bx
  1449.         push    cx
  1450.         push    dx
  1451.         push    si
  1452.         push    di
  1453.         push    bp
  1454.         push    es
  1455.  
  1456.         mov     tsr_req, 0            ; clear tsr request flag
  1457.  
  1458.         mov     ah, 0fh            ; ah = get current info
  1459.         int     10h             ; call BIOS
  1460.  
  1461.         push    bx                ; save current video page
  1462.  
  1463.         mov     ah, 3            ; ah = get cursor position
  1464.         int     10h             ; call BIOS
  1465.  
  1466.         push    dx                ; save cursor position
  1467.         push    cx                ; ..and cursor mode
  1468.  
  1469.         mov     ah, 1            ; ah = set cursor type
  1470.         mov     cx, 2020h            ; cx = no cursor
  1471.         int     10h             ; call BIOS
  1472.  
  1473.         call    swapdisp            ; q. swap video pages ok?
  1474.         jnc     tsr20            ; a. yes .. continue
  1475.  
  1476.         mov     ax, 0e07h            ; ax = write a beep
  1477.         int     10h             ; call BIOS
  1478.         jmp     short tsr90         ; ..and return to caller
  1479.  
  1480. tsr20:        mov     tsr_cnt, 0            ; clear tick counter
  1481.         call    dstat            ; display current status
  1482.  
  1483. tsr30:        call    chkprt            ; check printer queues
  1484.  
  1485.         call    dcmds            ; handle commands
  1486.         jc        tsr40            ; ..loop till exit requested
  1487.  
  1488.         cmp     tsr_cnt, 04         ; q. time to update screen?
  1489.         jb        tsr30            ; a. no .. just check queues
  1490.         jmp     short tsr20         ; else .. display status info
  1491.  
  1492. tsr40:        call    swapdisp            ; restore video pages
  1493.  
  1494. tsr90:        pop     cx                ; cx = original mode
  1495.         mov     ah, 1            ; ah = set cursor mode
  1496.         int     10h             ; .. do it
  1497.  
  1498.         pop     dx                ; dx = original position
  1499.         pop     bx                ; bx = video page nbr
  1500.         mov     ah, 2            ; ah = set positin
  1501.         int     10h             ; .. do it
  1502.  
  1503.         pop     es                ; restore registers
  1504.         pop     bp
  1505.         pop     di
  1506.         pop     si
  1507.         pop     dx
  1508.         pop     cx
  1509.         pop     bx
  1510.         pop     ax
  1511.         ret                 ; ..and return to caller
  1512.  
  1513. tsr        endp
  1514.  
  1515.  
  1516.  
  1517. ; ----------------------------------------------------------------------
  1518. ;   This routine displays each printers current status.
  1519. ; ----------------------------------------------------------------------
  1520.  
  1521. dstat        proc
  1522.         push    es                ; save register
  1523.         mov     bx, offset qhlpt1        ; bx -> 1st printer
  1524.  
  1525. dstat10:    mov     dl, [bx].lpstatus        ; dl = current status
  1526.  
  1527.         xor     di, di            ; di = no other message
  1528.  
  1529.         cmp     dl, 2            ; q. paused w/message?
  1530.         jne     dstat15            ; a. no .. continue
  1531.  
  1532.         mov     di, word ptr [bx].lpdqbuf + 2   ; di = seg of message
  1533.  
  1534. dstat15:    lea     si, dstat_msgs        ; si -> messages
  1535.         mov     cx, dstat_nbr        ; cx = msgs - 1
  1536.  
  1537. dstat20:    cmp     dl, [si]            ; q. is this our message
  1538.         jne     dstat30            ; a. no .. next one
  1539.  
  1540.         inc     si                ; si -> last message
  1541.         jmp     short dstat40        ; ..continue w/common code
  1542.  
  1543. dstat30:    mov     al, 1[si]            ; al = length of message
  1544.         cbw                 ; ax = length of message
  1545.         add     ax, 2            ; ax = len of msg block
  1546.         add     si, ax            ; si -> next message
  1547.  
  1548.         loop    dstat20            ; q. anymore messages?
  1549.  
  1550. dstat40:    cmp     dl, 0            ; q. use a canned message?
  1551.         jne     dstat50            ; a. yes .. continue
  1552.  
  1553.         call    dmbld            ; build the message
  1554.  
  1555. dstat50:    call    dmove            ; move message to display
  1556.  
  1557.         cmp     [bx].lpstatus, 2        ; q. paused w/comment?
  1558.         jne     dstat60            ; a. no .. continue
  1559.  
  1560.         les     si, [bx].lpdqbuf        ; es:si -> dqbuffer
  1561.         mov     cx, es:[di]         ; cx = string length
  1562.  
  1563.         cmp     cx, 35            ; q. comment need to scroll?
  1564.         jna     dstat60            ; a. no .. continue
  1565.  
  1566.         push    ds                ; save regs
  1567.  
  1568.         push    es                ; save dqbuf seg
  1569.         pop     ds                ; ds -> dqbuf
  1570.  
  1571.         mov     ah, ds:[2]            ; ah = first char
  1572.         mov     di, 2            ; di -> output area
  1573.         mov     si, 3            ; si -> input area
  1574.  
  1575.         dec     cx                ; cx = chars to move
  1576.         cld                 ; .. lower to higher
  1577.       rep   movsb                ; .. shift the string
  1578.  
  1579.         mov     [si-1], ah            ; save shifted char
  1580.  
  1581.         pop     ds                ; restore ds
  1582.  
  1583. dstat60:    add     bx, lplen            ; bx -> next printer
  1584.  
  1585.         cmp     bx, offset qhlpt3        ; q. done?
  1586.         jna     dstat10            ; a. no .. loop again
  1587.  
  1588.         pop     es                ; restore register
  1589.         ret                 ; return to caller
  1590. dstat        endp
  1591.  
  1592.  
  1593.  
  1594. ; ----------------------------------------------------------------------
  1595. ;   This routine builds the "printer ok" stats message.
  1596. ;
  1597. ;   Entry
  1598. ;    bx -> printer control block
  1599. ;    si -> message block
  1600. ; ----------------------------------------------------------------------
  1601.  
  1602. dmbld        proc
  1603.         push    si                ; save registers
  1604.         push    di                ;
  1605.         push    es
  1606.  
  1607.         push    bx                ; save lpt pointer
  1608.         mov     di, bx            ; ..and setup new ptr
  1609.  
  1610.         mov     ax, word ptr [di].lpticks    ; dx:ax = nbr of ticks
  1611.         mov     dx, word ptr [di].lpticks+2 ; ..
  1612.         mov     cx, 10            ; cx = multiplier
  1613.         call    mmul            ; scale number by 10
  1614.  
  1615.         mov     bx, 182            ; cx:bx = 18.2ticks/sec
  1616.         xor     cx, cx            ; ..
  1617.         call    ddiv            ; convert ticks to secs
  1618.  
  1619.         mov     cx, dx            ; cx:bx = seconds
  1620.         mov     bx, ax            ; ..
  1621.         mov     ax, word ptr [di].lpprted    ; dx:ax = printed chars
  1622.         mov     dx, word ptr [di].lpprted+2 ; ..
  1623.         call    ddiv            ; divide to get char/sec
  1624.  
  1625.         pop     bx                ; restore lpt pointer
  1626.         mov     [bx].lpcps, ax        ; save observed CPS rating
  1627.  
  1628. dmbld20:    xor     dx, dx            ; make into doubleword
  1629.         mov     di, si            ; di -> output area
  1630.         add     di, 22            ; di -> output area (lsb)
  1631.         call    dformat            ; convert to ascii
  1632.  
  1633.         mov     ax, word ptr [bx].lpprted    ; ax = part cnt
  1634.         mov     dx, word ptr [bx].lpprted+2 ; dx:ax = spooled chr
  1635.         mov     di, si            ; di -> output area
  1636.         add     di, 28            ; di -> output area (lsb)
  1637.         call    dformat            ; convert to ascii
  1638.  
  1639.         mov     ax, word ptr [bx].lpinque    ; ax = part cnt
  1640.         mov     dx, word ptr [bx].lpinque+2 ; dx:ax = chars in que
  1641.         mov     di, si            ; di -> output area
  1642.         add     di, 34            ; di -> output area (lsb)
  1643.         call    dformat            ; convert to ascii
  1644.  
  1645.         call    dgauge            ; build gauge & percent
  1646.  
  1647.         push    bx                ; save lp pointer
  1648.         mov     ax, word ptr [bx].lpinque    ; ax = part cnt
  1649.         mov     dx, word ptr [bx].lpinque+2 ; dx:ax = chrs in que
  1650.         mov     bx, [bx].lpcps        ; cx:bx = observed CPS
  1651.         xor     cx, cx            ; ..
  1652.         call    ddiv            ; get secs left to prt
  1653.         pop     bx                ; restore register
  1654.  
  1655.         mov     di, si            ; di -> output record
  1656.  
  1657.         or        dx, dx            ; q. > 65535 seconds?
  1658.         jnz     dmbld30            ; a. yes .. put up hours
  1659.  
  1660.         add     di, 43            ; di -> output area (lsb)
  1661.  
  1662.         mov     cx, 60            ; cx = divisor
  1663.         div     cx                ; ax = hr/min, dx = seconds
  1664.         call    ditoa            ; put out seconds
  1665.  
  1666.         xor     dx, dx            ; dx = 0 for division
  1667.         div     cx                ; ax = hours, dx = minutes
  1668.         call    ditoa            ; put out minutes
  1669.  
  1670.         mov     dx, ax            ; dx = hours
  1671.         call    ditoa            ; put out hours
  1672.  
  1673.         mov     byte ptr [si+38], ':'   ; patch up message
  1674.         mov     byte ptr [si+41], ':'
  1675.  
  1676.         jmp     short dmbld40        ; ..continue w/common code
  1677.  
  1678. dmbld30:    push    bx                ; save lp pointer
  1679.         mov     bx, 3600            ; cx:bx = seconds per hour
  1680.         xor     cx, cx            ; ..
  1681.         call    ddiv            ; get hours left to print
  1682.         pop     bx                ; restore register
  1683.  
  1684.         add     di, 38            ; di -> output area (lsb)
  1685.         call    dformat            ; put out hours
  1686.  
  1687.         mov     word ptr [si+40], 'H '  ; patch up message
  1688.         mov     word ptr [si+42], 'sr'  ;   with " Hrs"
  1689.  
  1690. dmbld40:    pop     es                ; restore registers
  1691.         pop     di                ;
  1692.         pop     si                ;
  1693.         ret                 ; then return to caller
  1694. dmbld        endp
  1695.  
  1696.  
  1697.  
  1698. ; ----------------------------------------------------------------------
  1699. ;   This routine prints the message to the screen.
  1700. ;
  1701. ;   Entry
  1702. ;    bx -> printer control block
  1703. ;    si -> message block
  1704. ;    es:di -> secondary message | 0
  1705. ; ----------------------------------------------------------------------
  1706.  
  1707. dmove        proc
  1708.         push    bx                ; save registers
  1709.         push    ds
  1710.  
  1711.         mov     ah, 2            ; ah = set cursor pos
  1712.         mov     dh, byte ptr [bx].lpprtnbr    ; dh = printer nbr
  1713.         inc     dh                ; dh = row number
  1714.         mov     dl, 5            ; dl = column number
  1715.         mov     bh, 0            ; bh = video page nbr
  1716.         int     10h             ; call BIOS
  1717.  
  1718.         xor     ch, ch            ; ch = 0
  1719.         mov     cl, [si]            ; cl = length
  1720.         push    cx                ; save message length
  1721.         inc     si                ; si -> message
  1722.  
  1723. dmove10:    mov     ah, 0eh            ; ah = write tty style
  1724.         lodsb                ; al = char to write
  1725.         int     10h             ; call BIOS to write char
  1726.         loop    dmove10            ; ..and loop till done
  1727.  
  1728.         or        di, di            ; q. any seconday message?
  1729.         jz        dmove20            ; a. no .. continue
  1730.  
  1731.         mov     ds, di            ; ds:0 -> rest of the story
  1732.         xor     si, si            ; si -> start of buffer
  1733.         xor     di, di            ; di = 0, clear flag
  1734.         lodsw                ; ax = length of msg
  1735.  
  1736.         cmp     ax, 34            ; q. greater than 34?
  1737.         jna     dmove15            ; a. no .. leave as is
  1738.  
  1739.         mov     ax, 34            ; .. max it at 42
  1740.  
  1741. dmove15:    pop     cx                ; restore 1st part
  1742.         add     cx, ax            ; cx = total length
  1743.  
  1744.         push    cx                ; save overall length
  1745.         mov     cx, ax            ; setup for loop
  1746.         jmp     dmove10            ; ..go back and put out msg
  1747.  
  1748. dmove20:    pop     ax                ; ax = last messages count
  1749.         mov     cx, 43            ; cx = max length
  1750.         sub     cx, ax            ; q. cx = len to blank out?
  1751.         jle     dmove40            ; a. nah .. nothin to do..
  1752.  
  1753.         mov     ax, 0e20h            ; ax = write blanks
  1754.  
  1755. dmove30:    int     10h             ; call BIOS to write char
  1756.         loop    dmove30            ; ..and loop till done
  1757.  
  1758. dmove40:    pop     ds                ; restore registers
  1759.         pop     bx
  1760.         ret                 ; then return to the caller
  1761.  
  1762. dmove        endp
  1763.  
  1764.  
  1765.  
  1766. ; ----------------------------------------------------------------------
  1767. ;   This routine handle the console commands
  1768. ;
  1769. ;   Returns
  1770. ;    carry = exit tsr requested
  1771. ; ----------------------------------------------------------------------
  1772.  
  1773. dcmds        proc
  1774.         mov     ah, 1            ; ah = check for keys fnc
  1775.         int     16h             ; q. anything there?
  1776.         jnz     dcmds10            ; a. yes .. continue
  1777.  
  1778.         clc                 ; clear carry
  1779.         ret                 ; ..and return to caller
  1780.  
  1781. dcmds10:    xor     ah, ah            ; ah = read key fnc
  1782.         int     16h             ; get a key
  1783.  
  1784.         cmp     al, 1bh            ; q. escape?
  1785.         jne     dcmds20            ; a. no .. continue
  1786.  
  1787.         stc                 ; set carry
  1788.         ret                 ; ..and return to caller
  1789.  
  1790. dcmds20:    or        al, al            ; q. alt key?
  1791.         jnz     dcmds22            ; a. no .. continue
  1792.  
  1793.         mov     dx, 2            ; dx = lpt3
  1794.         jmp     short dcmds30        ; ..goto common code
  1795.  
  1796. dcmds22:    cmp     al, ' '                 ; q. control key?
  1797.         ja        dcmds24            ; a. no .. unshifted
  1798.  
  1799.         mov     dx, 1            ; dx = lpt2
  1800.         jmp     short dcmds30        ; ..goto common code
  1801.  
  1802. dcmds24:    xor     dx, dx            ; dx = lpt1
  1803.  
  1804. dcmds30:    push    ax                ; save register
  1805.         mov     al, lplen            ; al = length of prt block
  1806.         mul     dl                ; ax = offset of req prt
  1807.         add     ax, offset qhlpt1        ; ax -> requested prt blk
  1808.         mov     bx, ax            ; bx -> lpt block
  1809.         pop     ax                ; restore register
  1810.  
  1811.         cmp     [bx].lpstatus, 0feh     ; q. non-existant printer?
  1812.         je        dcmds90            ; a. yes .. error
  1813.  
  1814.         cmp     ah, 20h            ; q. disable printer?
  1815.         jne     dcmds40            ; a. no .. try next cmd
  1816.  
  1817.         mov     [bx].lpstatus, 0ffh     ; set status to not started
  1818.         mov     [bx].lpcontrol, 0ffh    ; . . . .
  1819.         clc                 ; clear carry
  1820.         ret                 ; ..and return to caller
  1821.  
  1822. dcmds40:    cmp     ah, 21h            ; q. formfeed printer?
  1823.         jne     dcmds45            ; a. no .. try next cmd
  1824.  
  1825.         mov     ax, 000ch            ; ax = write a <ff>
  1826.         jmp     short dcmds95        ; ..goto common code to exit
  1827.  
  1828. dcmds45:    cmp     ah, 24h            ; q. job skip?
  1829.         jne     dcmds50            ; a. no .. try next cmd
  1830.  
  1831.         mov     ah, 0c5h            ; ah = job skip fnc
  1832.         int     17h             ; call BIOS
  1833.         jmp     short dcmds68        ; then release printer
  1834.  
  1835. dcmds50:    cmp     ah, 19h            ; q. pause printer?
  1836.         jne     dcmds55            ; a. no .. try next cmd
  1837.  
  1838.         cmp     [bx].lpstatus, 2        ; q. paused w/comment?
  1839.         je        dcmds53            ; a. yes .. leave it alone
  1840.  
  1841.         mov     [bx].lpstatus, 3        ; setup printer as paused
  1842.         mov     [bx].lpcontrol, 3        ; . . . .
  1843.  
  1844. dcmds53:    clc                 ; clear carry
  1845.         ret                 ; ..and return to caller
  1846.  
  1847. dcmds55:    cmp     ah, 13h            ; q. reset printer?
  1848.         jne     dcmds60            ; a. no .. try next cmd
  1849.  
  1850.         mov     ah, 1            ; ah = init printer
  1851.         jmp     short dcmds95        ; ..goto common code to exit
  1852.  
  1853. dcmds60:    cmp     ah, 2eh            ; q. cancel printer?
  1854.         jne     dcmds65            ; a. no .. try next cmd
  1855.  
  1856.         mov     ah, 0c3h            ; ah = cancel printer fnc
  1857.         int     17h             ; call BIOS
  1858.         jmp     short dcmds68        ; then release printer
  1859.  
  1860. dcmds65:    cmp     ah, 22h            ; q. "go" printer?
  1861.         jne     dcmds90            ; a. no .. try next cmd
  1862.  
  1863. dcmds68:    mov     [bx].lpcontrol, 0        ; make chkprt do a qread
  1864.         mov     [bx].lpstatus, 0        ; restart printer
  1865.         clc                 ; clear error flag
  1866.         ret                 ; ..ane return to caller
  1867.  
  1868. dcmds90:    mov     ax, 0e07h            ; ax = write a beep
  1869.         int     10h             ; call BIOS
  1870.         clc                 ; clear carry
  1871.         ret                 ; ..and return
  1872.  
  1873. dcmds95:    int     17h             ; call BIOS to do prt fnc
  1874.         clc                 ; clear carry
  1875.         ret                 ; ..and return
  1876.  
  1877. dcmds        endp
  1878.  
  1879.  
  1880.  
  1881. ; ----------------------------------------------------------------------
  1882. ;   This routine formats a doubleword integer into a 4 character
  1883. ;   output field.
  1884. ;
  1885. ;   Entry
  1886. ;    dx:ax = number to convert
  1887. ;    di -> lsb of output field
  1888. ;
  1889. ;   Result
  1890. ;    The output field is formatted according to the following rules.
  1891. ;
  1892. ;        0 < n < 10000      -->    nnnn
  1893. ;        10001 < n < 999k   -->    nnnK
  1894. ;        1m < n           -->    nnnM
  1895. ; ----------------------------------------------------------------------
  1896.  
  1897. dformat     proc
  1898.         push    bx                ; save register
  1899.  
  1900.         mov     cx, 4            ; cx = char loop count
  1901.  
  1902.         or        dx, dx            ; q. msb = 0?
  1903.         jnz     dform10            ; a. no .. not this range
  1904.  
  1905.         cmp     ax, 10000            ; q. lsb < 10000?
  1906.         jb        dform50            ; a. yes .. don't divide
  1907.  
  1908. dform10:    cmp     dx, 15            ; q. msb portion < 10k
  1909.         jg        dform30            ; a. no .. must be in mils
  1910.         jl        dform20            ; else .. in the thousands
  1911.  
  1912.         cmp     ax, 16960            ; q. lsb portion > 10k
  1913.         jae     dform30            ; a. yes .. setup divisor
  1914.  
  1915. dform20:    mov     bx, 1000            ; cx:bx = 1k divisor
  1916.         xor     cx, cx            ; . . .
  1917.         mov     byte ptr [di], "K"      ; move in the legend char
  1918.         jmp     short dform40        ; and continue w/common code
  1919.  
  1920. dform30:    mov     bx,16960            ; cx:bx = 1m divisor
  1921.         mov     cx,15            ; . . .
  1922.         mov     byte ptr [di], "M"      ; move in the legend char
  1923.  
  1924. dform40:    call    ddiv            ; do 32 bit divide here
  1925.  
  1926.         dec     di                ; di -> lsb of number
  1927.         mov     cx, 3            ; cx = char loop count
  1928.  
  1929. dform50:    mov     bx, 10            ; setup for itoa()
  1930.  
  1931. dform60:    div     bx                ; dx = digit for this place
  1932.         add     dx, '0'                 ; dl = ascii digit
  1933.         mov     [di], dl            ; store in output area
  1934.         xor     dl, dl            ; dl = 0 for rest of divides
  1935.         dec     di                ; di -> next location
  1936.  
  1937.         or        ax, ax            ; q. any more signif digits?
  1938.         je        dform70            ; a. no .. put in blanks
  1939.         loop    dform60            ; else .. loop till done
  1940.  
  1941. dform70:    dec     cx                ; q. blanks needed?
  1942.         jz        dform90            ; a. no .. all taken care of
  1943.  
  1944.         mov     dl, ' '                 ; dl = BL for lead suppress
  1945.  
  1946. dform80:    mov     [di], dl            ; store in output area
  1947.         dec     di                ; di -> next location
  1948.         loop    dform80            ; loop lead blanking string
  1949.  
  1950. dform90:    pop     bx                ; restore registers
  1951.         ret                 ; return to caller
  1952. dformat     endp
  1953.  
  1954.  
  1955.  
  1956. ; ----------------------------------------------------------------------
  1957. ;   This routine converts a binary number into two printable ascii
  1958. ;   characters.
  1959. ;
  1960. ;   Entry
  1961. ;    dx = nbr to convert
  1962. ;    di -> lsb of output
  1963. ; ----------------------------------------------------------------------
  1964.  
  1965. ditoa        proc
  1966.         push    ax                ; save registers
  1967.  
  1968.         mov     ax, dx            ; ax = number
  1969.         aam                 ; convert to two packed nbrs
  1970.         or        ax, 3030h            ; ax = ascii characters
  1971.         xchg    ah, al            ; swap around for store
  1972.         sub     di, 3            ; di -> next lsb
  1973.         mov     word ptr [di+2], ax     ; save ascii characters
  1974.  
  1975.         pop     ax                ; restore registers
  1976.         ret                 ; ..and return to caller
  1977.  
  1978. ditoa        endp
  1979.  
  1980.  
  1981.  
  1982. ; ----------------------------------------------------------------------
  1983. ;   This routine determines the percentage of use of the queue area.
  1984. ;
  1985. ;   Entry
  1986. ;    bx -> printer control block
  1987. ;    si -> message line
  1988. ; ----------------------------------------------------------------------
  1989.  
  1990. dgauge        proc
  1991.         push    bx                ; save registers
  1992.  
  1993.         mov     ax, word ptr [bx].lpinque    ; get chars in spool
  1994.         mov     dx, word ptr [bx].lpinque+2 ; dx:ax = in spool
  1995.         mov     cx, 100            ; cx = multiplier
  1996.         call    mmul            ; dx:ax *= 100
  1997.         push    ax                ; save product
  1998.         push    dx
  1999.  
  2000.         mov     ax, nextqrnbr        ; ax = nbr of recs in queue
  2001.         xor     dx, dx            ; dx:ax = nbr of recs
  2002.         mov     cx, qrdlen            ; cx = size of qrecord
  2003.         mul     cx                ; dx:ax = que rec space avail
  2004.  
  2005.         add     ax, lpt_dqsz        ; add in memory puddle size
  2006.         adc     dx, 0            ; dx:ax = bytes available
  2007.  
  2008.         mov     cx, dx            ; setup for 32bit division
  2009.         mov     bx, ax            ; cx:bx = divisor
  2010.         pop     dx                ; get dividend
  2011.         pop     ax
  2012.         call    ddiv            ; divide to get percent used
  2013.  
  2014.         cmp     ax, 100            ; q. greater than 100%
  2015.         jna     dgauge10            ; a. no .. continue
  2016.  
  2017.         mov     ax, 100            ; ax = max of 100%
  2018.  
  2019. dgauge10:   push    ax                ; save for later
  2020.  
  2021.         mov     di, si            ; di -> output string
  2022.         add     di, 4            ; di -> output field
  2023.         call    dformat            ; put in percentage
  2024.  
  2025.         pop     ax                ; restore register
  2026.  
  2027.         push    es                ; save register
  2028.  
  2029.         push    ds                ; setup addressibility
  2030.         pop     es                ; ..to our string
  2031.  
  2032.         mov     di, si            ; di -> output string
  2033.         add     di, 7            ; di -> output field
  2034.  
  2035.         xor     dx, dx            ; dx = 0 for the divide
  2036.         mov     bx, 10            ; bl = divisor for gauge
  2037.         div     bx                ; ax = quotient, dx = rmdr
  2038.  
  2039.         push    ax                ; save nbr of whole blks
  2040.         xor     cx, cx            ; assume no chars prtd
  2041.  
  2042.         or        ax, dx            ; q. any chars needed?
  2043.         pop     ax                ; .. restore count
  2044.         jz        dgauge40            ; a. no .. blank it.
  2045.  
  2046.         push    ax                ; .. save again
  2047.  
  2048.         or        ax, ax            ; q. any whole blks?
  2049.         jz        dgauge30            ; a. no .. try parts
  2050.  
  2051.         mov     cx, ax            ; cx = loop count
  2052.         mov     al, 219            ; al = whole block char
  2053.  
  2054. dgauge20:   cld                 ; assure direction fwd
  2055.        rep  stosb                ; put in whole blocks
  2056.  
  2057. dgauge30:   pop     cx                ; restore whole count
  2058.  
  2059.         or        dx, dx            ; q. any partials?
  2060.         jz        dgauge40            ; a. no .. blank rest
  2061.  
  2062.         cmp     dx, 5            ; q. partial?
  2063.         jbe     dgauge35            ; a. yes .. setup for one
  2064.  
  2065.         mov     byte ptr [di], 219        ; else .. another whole
  2066.         jmp     short dgauge37        ; put out char
  2067.  
  2068. dgauge35:   mov     byte ptr [di], 221        ; put out partial char
  2069.  
  2070. dgauge37:   inc     di                ; di -> next output location
  2071.         inc     cx                ; ax = output'd length
  2072.  
  2073. dgauge40:   neg     cx                ; cx = minus max length
  2074.         add     cx, 10            ; cx = nbr blanks
  2075.         mov     al, '-'                 ; al = blanking character
  2076.  
  2077.      repnz  stosb                ; blank remainder of line
  2078.  
  2079.         pop     es                ; restore registers
  2080.         pop     bx
  2081.         ret                 ; ..and return to caller
  2082.  
  2083. dgauge        endp
  2084.  
  2085.  
  2086.  
  2087. ; ----------------------------------------------------------------------
  2088. ;   This routine does a 32bit unsigned division.
  2089. ;
  2090. ;   Entry
  2091. ;    dx:ax = dividend
  2092. ;    cx:bx = divisor
  2093. ;
  2094. ;   Exit
  2095. ;    dx:ax = quotient
  2096. ; ----------------------------------------------------------------------
  2097.  
  2098. ddiv        proc
  2099.         push    cx                ; make a work area    [bp + 8]
  2100.         push    bx                ;  from the stack    [bp + 6]
  2101.         push    dx                ;            [bp + 4]
  2102.         push    ax                ;            [bp + 2]
  2103.  
  2104.         push    bp                ; save registers
  2105.         mov     bp,sp            ; setup arg pointer
  2106.         push    si                ;
  2107.  
  2108.         or        bx, cx            ; q. dividing by zero?
  2109.         jnz     ddiv2            ; a. no .. continue
  2110.  
  2111.         xor     ax, ax            ; else .. return a zero
  2112.         xor     dx, dx            ; ...
  2113.         jmp     short ddiv7         ; return to caller
  2114.  
  2115. ddiv2:        mov     ax, cx            ; ax = divisor msb
  2116.  
  2117.         or        ax, ax            ; q. 32bit / 16bit?
  2118.         jnz     ddiv3            ; a. no .. 32bit / 32bit
  2119.  
  2120.                         ; 32bit / 16bit divide
  2121.         mov     cx, [bp + 6]        ; cx = divisor lsb
  2122.         mov     ax, [bp + 4]        ; ax = dividend msb
  2123.         xor     dx, dx            ; dx = 0
  2124.         div     cx                ; dx:ax = msb / lsb
  2125.         mov     bx, ax            ; bx = temp result
  2126.         mov     ax, [bp + 2]        ; ax = dividend lsb
  2127.         div     cx                ; dx:ax = temp result
  2128.         mov     dx, bx            ; dx = temp msb result
  2129.         jmp     ddiv7            ; ..return to call thru exit
  2130.  
  2131.                         ; 32bit / 32bit divide
  2132. ddiv3:        mov     bx, ax            ; bx = divisor msb
  2133.         mov     cx, [bp + 6]        ; cx = divisor lsb
  2134.         mov     dx, [bp + 4]        ; dx = dividend msb
  2135.         mov     ax, [bp + 2]        ; ax = dividend lsb
  2136.  
  2137. ddiv4:        shr     bx, 1            ; shift reduction
  2138.         rcr     cx, 1            ;    of both 32bit operands
  2139.         shr     dx, 1            ; . . .
  2140.         rcr     ax, 1            ; . . .
  2141.  
  2142.         or        bx, bx            ; q. done reducing?
  2143.         jnz     ddiv4            ; a. no .. loop back
  2144.  
  2145.         div     cx                ; dx:ax = 1st result
  2146.         mov     si, ax            ; si = save temp
  2147.         mul     word ptr [bp + 8]        ;
  2148.         xchg    cx, ax            ; swap temp values
  2149.         mov     ax, [bp + 6]        ; ax = divisor lsb
  2150.         mul     si                ;
  2151.         add     dx, cx            ;
  2152.         jb        ddiv5            ;
  2153.  
  2154.         cmp     dx, [bp + 4]        ;
  2155.         ja        ddiv5            ;
  2156.         jb        ddiv6            ;
  2157.  
  2158.         cmp     ax, [bp + 2]        ; q. result < original lsb?
  2159.         jbe     ddiv6            ; a. yes .. exit here
  2160.  
  2161. ddiv5:        dec     si                ; di =
  2162. ddiv6:        xor     dx, dx            ; dx = 0
  2163.         mov     ax, si            ; ax = result
  2164.  
  2165. ddiv7:        pop     si                ; restore registers
  2166.         pop     bp                ;
  2167.         add     sp, 8            ; adjust stack for work area
  2168.         ret                 ; ..and return to caller
  2169. ddiv        endp
  2170.  
  2171.  
  2172. ; ----------------------------------------------------------------------
  2173. ;   This routine does a 32bit by 16bit multiply
  2174. ;
  2175. ;   Entry
  2176. ;    dx:ax = multiplier
  2177. ;       cx = multiplicand
  2178. ;
  2179. ;   Exit
  2180. ;    dx:ax = product
  2181. ; ----------------------------------------------------------------------
  2182.  
  2183. mmul        proc
  2184.         push    si                ; save regs
  2185.         push    di
  2186.  
  2187.         mov     di, dx            ; save upper value
  2188.         mul     cx                ; .. multiply lower
  2189.         push    ax                ; .. save lower product
  2190.         mov     si, dx            ; .. save upper product
  2191.  
  2192.         mov     ax, di            ; ax = upper value
  2193.         mul     cx                ; multiply upper
  2194.         mov     dx, ax            ; dx = partial product
  2195.         add     dx, si            ; dx = full upper product
  2196.         pop     ax                ; dx:ax = 32 bit product
  2197.  
  2198.         pop     di                ; restore regs
  2199.         pop     si
  2200.  
  2201.         ret
  2202. mmul        endp
  2203.  
  2204.  
  2205. ; ----------------------------------------------------------------------
  2206. ;
  2207. ;   qh - queue handler, device independant queue interface routine
  2208. ;
  2209. ;   Read a queue record
  2210. ;
  2211. ;   This function is used to get a buffer off of a printer's queue.
  2212. ;   The printer queues are fifo queues.
  2213. ;
  2214. ;   On return, the carry flag indicates one of the following:
  2215. ;    - There are no more records on the specified printer queue
  2216. ;    - An invalid queue number was provided.
  2217. ;
  2218. ;   When an expandable buffer (disk, EMS) is used and freepool is empty,
  2219. ;   one or more new records will be allocated at the end of the queue.
  2220. ;   Reading a record automatically puts it on the freepool queue.
  2221. ;
  2222. ;   Entry: ah = 0
  2223. ;       al = queue to read (0,1,2 = lpt1,2,3)
  2224. ;       ds:dx -> buffer
  2225. ;
  2226. ;   Exit:  bx = queue record # read
  2227. ;       carry = error
  2228. ;       al = exit code, 00 = success
  2229. ;               01 = invalid queue number
  2230. ;               02 = no data on queue
  2231. ;               03 = unable to read at this time.
  2232. ;
  2233. ;
  2234. ;   Write a queue record
  2235. ;
  2236. ;   This function is used to add a queue record to a printer queue. This
  2237. ;   function is performed by:
  2238. ;    - get a free record from queue (allocate if needed)
  2239. ;    - writing the new record
  2240. ;    - updating the chains
  2241. ;
  2242. ;   On return, the carry flag indicates success or failure.
  2243. ;   The function may fail for one of the following reasons:
  2244. ;    - no more space in queue
  2245. ;    - unable to write queue record (DOS in use)
  2246. ;    - invalid queue number
  2247. ;
  2248. ;   Entry: ah = 1
  2249. ;       al = queue to write (0,1,2 = lpt1,2,3)
  2250. ;       ds:dx -> buffer
  2251. ;
  2252. ;   Exit:  carry = error
  2253. ;       al = exit code, 00 = success
  2254. ;               01 = invalid queue number
  2255. ;               02 = space exhausted
  2256. ;               03 = unable to write at this time.
  2257. ;
  2258. ; ----------------------------------------------------------------------
  2259.  
  2260. qh        proc
  2261.         push    bx                ; save registers
  2262.         push    cx
  2263.         push    dx
  2264.  
  2265.         inc     qinuseflg            ; show qh in use
  2266.  
  2267.         cmp     al, 2            ; q. valid queue number?
  2268.         jna     qh00            ; a. yes .. continue
  2269.  
  2270.         mov     al, 1            ; al = invalid queue error
  2271.         jmp     short qh92            ; .. return w/error
  2272.  
  2273. qh00:        or        ah, ah            ; q. read?
  2274.         jnz     qh01            ; a. no .. skip the call
  2275.  
  2276.         call    qhr             ; else .. read a record
  2277.         jmp     short qh95            ; .. and return to caller
  2278.  
  2279. qh01:        dec     ah                ; q. write?
  2280.         jnz     qh90            ; a. no .. bad function
  2281.  
  2282.         call    qhw             ; else .. write a record
  2283.         jmp     short qh95            ; .. and return to caller
  2284.  
  2285. qh90:        mov     al, 0ffh            ; al = bad function request
  2286. qh92:        stc                 ; .. carry on
  2287.  
  2288. qh95:        dec     qinuseflg            ; let someone else use rtn
  2289.  
  2290.         pop     dx                ; restore registers
  2291.         pop     cx
  2292.         pop     bx
  2293.         ret                 ; return to caller
  2294.  
  2295. qh        endp                ; end of queue handler main
  2296.  
  2297. ; ---------------------
  2298. ;   read a queue record
  2299. ; ---------------------
  2300.  
  2301. qhr        proc
  2302.         mov     bl, lplen            ; bl = length of lpstruc
  2303.         mul     bl                ; ax = offset of printer CB
  2304.  
  2305.         mov     bx, ax            ; bx => requested queue
  2306.         add     bx, offset qhlpt1        ; bx -> requested queue
  2307.  
  2308.         cmp     [bx].lpfirstqr, 0        ; q. records on queue?
  2309.         jne     qhr05            ; a. yes .. get first record
  2310.  
  2311.         mov     al, 2            ; al = no data on queue
  2312.         jmp     short qhr90         ; .. return w/error
  2313.  
  2314. qhr05:        mov     cx, [bx].lpfirstqr        ; cx = next queue record
  2315.         mov     ax, 1            ; ax = read queue record
  2316.         call    dword ptr qrtn        ; q. get go ok?
  2317.         jnc     qhr10            ; a. yes .. update queues
  2318.  
  2319.         mov     al, 3            ; al = can't read now
  2320.         jmp     short qhr90         ; .. return w/error
  2321.  
  2322. qhr10:        mov     si, dx            ; si -> record just read
  2323.         mov     ax, [si].qrnextqr        ; ax = nxt rec nbr on lp que
  2324.  
  2325.         mov     [bx].lpfirstqr, ax        ; remove record from lp que
  2326.  
  2327.         or        ax, ax            ; q. end of queue?
  2328.         jnz     qhr20            ; a. no .. add to freepool
  2329.  
  2330.         mov     [bx].lplastqr, ax        ; else .. free last lp rec
  2331.  
  2332. qhr20:        mov     ax, qhfirstf        ; ax -> first free record
  2333.         mov     qhfirstf, cx        ; first free = record read
  2334.         mov     [si].qrnextqr, ax        ; queue record = new next
  2335.  
  2336.         mov     ax, 4            ; ax = rewrite ptr as free
  2337.         call    dword ptr qrtn        ; .. write to queue
  2338.  
  2339.         xor     al, al            ; al = all is well
  2340.         clc                 ; .. no carry
  2341.         ret                 ; return to caller
  2342.  
  2343. qhr90:        stc                 ; show error
  2344.         ret                 ; .. return to caller
  2345.  
  2346. qhr        endp
  2347.  
  2348. ; ---------------------
  2349. ;  write a queue record
  2350. ; ---------------------
  2351.  
  2352. qhw        proc                ; write a queue record
  2353.  
  2354.         push    dx                ; save buffer pointer
  2355.  
  2356.         mov     bl, lplen            ; bl = length of lpstruc
  2357.         mul     bl                ; ax = offset of printer CB
  2358.  
  2359.         mov     bx, ax            ; bx -> requested queue
  2360.         add     bx, offset qhlpt1        ; bx -> requested queue
  2361.  
  2362.         mov     cx, qhfirstf        ; q. any free records
  2363.         jcxz    qhw10            ; a. no .. allocate new one
  2364.  
  2365.         mov     dx, offset qhfirstf     ; dx -> new first free
  2366.         mov     ax, 3            ; ax = read ptr of 1st free
  2367.         call    dword ptr qrtn        ; q. read pointer ok?
  2368.         jnc     qhw20            ; a. yes .. continue
  2369.  
  2370.         mov     al, 3            ; al = can't right now.
  2371.         mov     qhfirstf, cx        ; put record on freepool
  2372.         pop     dx                ; restore buffer pointer
  2373.         jmp     short qhw90         ; ..then return w/error
  2374.  
  2375. qhw10:        mov     ax, 5            ; ax = allocate new record
  2376.         call    dword ptr qrtn        ; q. new record available?
  2377.         jnc     qhw20            ; a. yes .. continue
  2378.  
  2379.         mov     al, 3            ; al = can't write now
  2380.         pop     dx                ; .. restore buffer pointer
  2381.         jmp     short qhw90         ; .. return w/error
  2382.  
  2383. qhw20:        pop     si                ; si -> record, cx = rec nbr
  2384.         mov     dx, si            ; dx -> our record
  2385.         mov     [si].qrnextqr, 0        ; .. set last record pointer
  2386.         mov     ax, 2            ; ax = write our record
  2387.         call    dword ptr qrtn        ; q. write ok?
  2388.         jnc     qhw25            ; a. yes .. continue
  2389.  
  2390.         mov     al, 3            ; al = can't write now.
  2391.         jmp     short qhw90         ; .. return w/error
  2392.  
  2393. qhw25:        cmp     [bx].lpfirstqr, 0        ; q. queue empty?
  2394.         je        qhw30            ; a. yes .. make 1st & last
  2395.  
  2396.         xchg    cx, [bx].lplastqr        ; cx <-> last on queue
  2397.         mov     ax, 4            ; ax = write pointer only
  2398.         lea     dx, [bx].lplastqr        ; dx -> new last record nbr
  2399.         call    dword ptr qrtn        ; .. update prev last record
  2400.         jmp     short qhw80         ; .. return ok
  2401.  
  2402. qhw30:        mov     [bx].lpfirstqr, cx        ; set as first record
  2403.         mov     [bx].lplastqr, cx        ; .. and last record
  2404.  
  2405. qhw80:        xor     al, al            ; al = no problem
  2406.         clc                 ; .. no carry
  2407.         ret                 ; .. return to caller
  2408.  
  2409. qhw90:        stc                 ; carry .. problem
  2410.         ret                 ; .. return to caller
  2411.  
  2412. qhw        endp
  2413.  
  2414. ; ---------------------------------------------------------------------
  2415. ;   Swap display with buffer memory;
  2416. ;
  2417. ;                Returns: carry if bad video mode mode
  2418. ; ---------------------------------------------------------------------
  2419. swapdisp    proc
  2420.         push    bp                ; save registers
  2421.  
  2422.         mov     ah, 0fh            ; get current display mode
  2423.         int     10h             ; .. ask BIOS
  2424.  
  2425.         cmp     al, 2            ; q. BW mode?
  2426.         je        swpdsp10            ; a. yes .. ok to do it.
  2427.  
  2428.         cmp     al, 3            ; q. Color mode?
  2429.         je        swpdsp10            ; a. yes .. ok to do it.
  2430.  
  2431.         cmp     al, 7            ; q. Mono mode?
  2432.         je        swpdsp10            ; a. yes .. ok to do it.
  2433.  
  2434.         stc                 ; show error
  2435.         jmp     short swpdsp90        ; .. return now
  2436.  
  2437. swpdsp10:   mov     si, offset scrbuf        ; si -> screen buffer
  2438.         mov     di, lines            ; di = rows to swap
  2439.         xor     dh, dh            ; dh = start on line 0
  2440.  
  2441. swpdsp15:   mov     cx, cols            ; columns to swap
  2442.         xor     dl, dl            ; dl = start at pos 0
  2443.  
  2444. swpdsp20:   mov     ah, 2            ; ah = set cursor position
  2445.         int     10h             ; .. set the cursor
  2446.  
  2447.         mov     ah, 8            ; ah = read char & attr
  2448.         int     10h             ; .. well.. do it
  2449.  
  2450.         xchg    ax, [si]            ; swap with buffer
  2451.         inc     si                ; si -> next
  2452.         inc     si                ; .. char
  2453.  
  2454.         push    cx                ; save counter
  2455.         mov     bl, ah            ; bl = attr to write
  2456.         mov     ah, 9            ; ah = write char @ cursor
  2457.         mov     cx, 1            ; cx = # to write
  2458.         int     10h             ; .. write char
  2459.         pop     cx                ; .. restore counter
  2460.  
  2461.         inc     dl                ; dl = next column
  2462.         loop    swpdsp20            ; .. loop 'til all line done
  2463.  
  2464.         inc     dh                ; dh = next line
  2465.         dec     di                ; q. all lines done?
  2466.         jnz     swpdsp15            ; a. no .. next line
  2467.  
  2468.         clc                 ; clear error flag
  2469.  
  2470. swpdsp90:   pop     bp                ; restore registers
  2471.         ret                 ; return to caller
  2472.  
  2473. swapdisp    endp
  2474.  
  2475. ; ----------------------------------------------------------------------
  2476. ;   Start routine;        startup processing for spool
  2477. ;
  2478. ;   This routine performs the following functions:
  2479. ;    - Call CMD to perform initial processing
  2480. ;    - Move the qrtn to qhandler
  2481. ;    - Set the final qrtn address
  2482. ;    - Calculate paragraphs to keep (including CONV MEM if needed)
  2483. ;    - Set all interrupt vectors
  2484. ;    - Go TSR
  2485. ; ----------------------------------------------------------------------
  2486. start        proc
  2487.         push    ds                ; save entry ds
  2488.         call    cmd             ; initialize the system
  2489.      rep    movsb                ; .. move the qhandler rtn
  2490.         pop     ds                ; restore ds
  2491.  
  2492.         mov     qrtnseg, dx         ; setup new qhandler
  2493.  
  2494.         mov     dx, nxtavail        ; dx = bytes used
  2495.         add     dx, 15            ; .. next para if needed
  2496.         mov     cl, 4            ; cl = shift count
  2497.         shr     dx, cl            ; dx = paragraph count
  2498.  
  2499.         test    pcsflg, pcsflgcm        ; q. conventional spool?
  2500.         jz        start50            ; a. no addtl allocation
  2501.  
  2502.         mov     ax, cs            ; ax = start paragraph
  2503.         add     ax, dx            ; ax = para of mem queue
  2504.         mov     memqseg, ax         ; save segment of conv area
  2505.  
  2506.         mov     ax, memqsize        ; ax = conv mem size
  2507.         mov     bl, (qrlen/16)        ; bl = paragraphs/qrecord
  2508.         mul     bl                ; ax = paragraphs for queue
  2509.         add     dx, ax            ; dx = paragraphs to keep
  2510.  
  2511. start50:    push    dx                ; save memory to keep
  2512.  
  2513.         mov     cx, clrlen            ; cx = length to clear
  2514.         mov     di, startclr        ; di -> where to clear
  2515.         mov     al, 0            ; al = initial memory value
  2516.     rep stosb                ; clear allocated areas
  2517.  
  2518.         mov     si, intblk1         ; si -> 1st interrupt block
  2519.         mov     cx, intblkcnt        ; cx = nbr of ints handled
  2520.  
  2521. start60:    mov     ah, 35h            ; ah = get interrupt vector
  2522.         mov     al, [si+12]         ; al = interrupt number
  2523.         int     21h             ; .. get the current setting
  2524.  
  2525.         mov     [si+2], es            ; save segment of old int
  2526.         mov     [si], bx            ; .. and offset
  2527.  
  2528.         mov     dx, [si+13]         ; dx -> new interrupt
  2529.         mov     ah, 25h            ; ah = set interrupt vector
  2530.         int     21h             ; .. set up new vector
  2531.  
  2532.         add     si, intblklen        ; si -> next entry
  2533.         loop    start60            ; .. set next interrupt
  2534.  
  2535.         dec     i21hflg            ; open flood gate for int 21
  2536.         pop     dx                ; restore memory to keep
  2537.  
  2538.         mov     ax, 3100h            ; ax = TSR, rc = 0
  2539.         int     21h             ; .. hope & pray
  2540.  
  2541. start        endp
  2542.  
  2543.         align   16                ; align to a paragraph
  2544.  
  2545. ; ---------------------------------------------------------------------
  2546. ;   Screen buffer
  2547. ; ---------------------------------------------------------------------
  2548.  
  2549. scrbuf        db        '║ LPT      Gauge        CPS    CP   CIQ '
  2550.         db        '  Time   │ D: Disable   P: Pause  G:Go ║'
  2551.         db        '║  1:                                   '
  2552.         db        '         │ F: Formfeed  R: Reset   Esc ║'
  2553.         db        '║  2:                                   '
  2554.         db        '         │ J: JobSkip   C: Cancel      ║'
  2555.         db        '║  3:                                   '
  2556.         db        '         │ CTRL+key:LPT2, ALT+key:LPT3 ║'
  2557.         db        '╚═══════════════════════════════════════'
  2558.         db        '═════════╧═════════════════════════════╝'
  2559. scrbufcon   equ     $-1
  2560.  
  2561.  
  2562. ; ----------------------------------------------------------------------
  2563. ;   Note: Everything beyond this is overlaid after initialization.
  2564. ; ----------------------------------------------------------------------
  2565.  
  2566. scrbufend   equ     scrbufcon + 400
  2567.  
  2568. qhandler    equ     scrbuf + 800
  2569.  
  2570. ; ----------------------------------------------------------------------
  2571. ;   The queue handler will be moved to this address after initialization
  2572. ; ----------------------------------------------------------------------
  2573.  
  2574. ; ----------------------------------------------------------------------
  2575. ;   Initialization code data areas --  available during init only
  2576. ; ----------------------------------------------------------------------
  2577.  
  2578. hdrmsg        db    "PCSPOOL V1.0  (c) 1990, Ziff Communications Co.",13,10
  2579.         db    "PC Magazine ",254," Michael Holmes and Bob Flanders"
  2580.         db    13,10
  2581. dollar        db    "$"
  2582.  
  2583. help        db    10
  2584.         db    "Usage:",13,10,10
  2585.         db    "   PCSPOOL /I [/1] [/2] [/3] [/Cnn|/D[d:\path\]]",13,10
  2586.         db    "   PCSPOOL /P [/1|/2|/3] [Comment ..]",13,10
  2587.         db    "   PCSPOOL /F [/1|/2|/3]",13,10
  2588.         db    "   PCSPOOL /U",13,10,"$"
  2589.  
  2590. invreq        db    10
  2591.         db    "Invalid request - "
  2592. invreqcd    db    " ",13,10,"$"
  2593.  
  2594. invopnd     db    10
  2595.         db    "Invalid operand - "
  2596. invopndcd   db    " ",13,10,"$"
  2597.  
  2598. cantinitq   db    10
  2599.         db    "Unable to initialize queue.",13
  2600. notup        db    10
  2601.         db    "Spooler not installed.",13,10,"$"
  2602.  
  2603. upalrdy     db    10
  2604.         db    "Spooler already installed.",13,10,"$"
  2605.  
  2606. notspld     db    10
  2607.         db    "Requested printer not spooled.",13,10,"$"
  2608.  
  2609. nodesq        db    10
  2610.         db    "This utility does not work with multitasking."
  2611.         db    13,10,"$"
  2612.  
  2613. splalrdy    db    10
  2614.         db    "You may only specify 1 spool type.",13,10,"$"
  2615.  
  2616. invmemz     db    10
  2617.         db    "Invalid memory size specified. "
  2618.         db    "Must be 1 to 64.",13,10,"$"
  2619.  
  2620. baddrive    db    10
  2621.         db    "Invalid drive specified.",13,10,"$"
  2622.  
  2623. cantfree    db    10
  2624.         db    "Can't uninstall at this time.",13,10,"$"
  2625.  
  2626. freeok        db    10
  2627.         db    "Uninstalled successfully.",13,10,"$"
  2628.  
  2629. pauseok     db    10
  2630.         db    "Pause issued successfully.",13,10,"$"
  2631.  
  2632. waitmsg     db    10
  2633.         db    "Waiting for print to complete. ESC cancels shutdown."
  2634.         db    13,10
  2635.         db    "Hit any other key to uninstall immediately.",13,10,"$"
  2636.  
  2637. uninscan    db    10
  2638.         db    "Uninstall cancelled.  PCSPOOL still resident."
  2639.         db    13,10,"$"
  2640.  
  2641. ffok        db    10
  2642.         db    "Formfeed sent to printer.",13,10,"$"
  2643.  
  2644. qfilenm     db    "PCSPOOL.QUE",0             ; Q's filename
  2645.  
  2646. char_C        db    "C"                         ; default to conventional
  2647.  
  2648. ; ----------------------------------------------------------------------
  2649. ;
  2650. ;   Initialization procedure;        cs, ds, es -> our segment
  2651. ;
  2652. ;                Return: ds:si -> queue routine
  2653. ;                    es:di -> new address
  2654. ;                       cx =  len to move
  2655. ;                       dx =  segment of new addr
  2656. ;
  2657. ;   1. Scan for a commmand
  2658. ;    - if error, issue error message & exit
  2659. ;
  2660. ;   2. Call appropriate command processor
  2661. ;
  2662. ;   3. Setup regs for move of queue handler routine
  2663. ;
  2664. ; ----------------------------------------------------------------------
  2665.  
  2666. cmd        proc                ; find/process command
  2667.         push    es                ; save regs
  2668.  
  2669.         mov     dx, offset hdrmsg        ; ds:dx -> header message
  2670.         mov     ah, 9            ; ah = print ASCII$ string
  2671.         int     21h             ; .. display header
  2672.  
  2673.  
  2674.         mov     cx, 'DE'                ; cx = DE of DESQ
  2675.         mov     dx, 'SQ'                ; dx = SQ of DESQ
  2676.         mov     ax, 2b01h            ; ax = set invalid date
  2677.         int     21h             ; .. let DOS have at it
  2678.  
  2679.         cmp     al, 0ffh            ; q. desqview active?
  2680.         je        cmd0            ; a. no .. continue
  2681.  
  2682.         mov     dx, offset nodesq        ; dx -> not desqview compatible
  2683.         xor     al, al            ; al = no help needed
  2684.         call    die             ; .. die .. with honor
  2685.  
  2686. cmd0:        mov     ah, 0c4h            ; ah = query spooler active
  2687.         int     17h             ; q. spooler running?
  2688.  
  2689.         mov     bx, 81h            ; bx -> command line
  2690.  
  2691.         call    nxtop            ; q. any operands?
  2692.         jnc     cmd00            ; a. yes .. process
  2693.  
  2694.         mov     dx, offset dollar        ; dx -> null message
  2695.         mov     al, 1            ; al = give help
  2696.         call    die             ; terminal w/help
  2697.  
  2698. cmd00:        cmp     al, 'I'                 ; q. Init request?
  2699.         jnz     cmd10            ; a. no .. check next
  2700.  
  2701.         call    init            ; else.. call init code
  2702.         jmp     short cmd90         ; return to caller
  2703.  
  2704. cmd10:        cmp     al, 'P'                 ; q. pause request?
  2705.         jnz     cmd20            ; a. no .. check next
  2706.  
  2707.         jmp     pause            ; else.. goto pause rtn
  2708.  
  2709. cmd20:        cmp     al, 'U'                 ; q. uninstall?
  2710.         jnz     cmd30            ; a. no .. check next
  2711.  
  2712.         jmp     uninstall            ; else.. call  uninstall
  2713.  
  2714. cmd30:        cmp     al, 'F'                 ; q. formfeed?
  2715.         jnz     cmd40            ; a. no .. error
  2716.  
  2717.         jmp     formfeed            ; else.. send an ff
  2718.  
  2719. cmd40:        mov     invreqcd, al        ; save error code
  2720.  
  2721.         mov     dx, offset invreq        ; ds:dx -> error message
  2722.         mov     al, 1            ; al = give help
  2723.         call    die             ; Message & die
  2724.  
  2725. cmd90:        pop     es                ; restore regs
  2726.  
  2727.         lea     di, qhandler        ; di -> qhandler rtn
  2728.  
  2729.         mov     dx, di            ; dx = offset also
  2730.         mov     cl, 4            ; cl = shift amount
  2731.         shr     dx, cl            ; dx = segment offset
  2732.         mov     ax, cs            ; ax = program segment
  2733.         add     dx, ax            ; dx = qhandler segment
  2734.  
  2735.         xor     si, si            ; si = 0 offset
  2736.         mov     cx, qrtnlen         ; cx = size of routine
  2737.         cld                 ; direction = up
  2738.  
  2739.         mov     ds, qrtnseg         ; ds = current seg of rtn
  2740.  
  2741.         ret                 ; return to caller
  2742.  
  2743. cmd        endp                ; end of scan routine
  2744.  
  2745. ; ----------------------------------------------------------------------
  2746. ;   nxtop: find next operand;  bx -> current position
  2747. ;
  2748. ;               Returns: bx -> 1st char (after slash) of next op
  2749. ;                al =  1st letter of opnd (capitalized)
  2750. ;                carry = end of line
  2751. ; ----------------------------------------------------------------------
  2752.  
  2753. nxtop        proc
  2754.         cmp     byte ptr [bx], 0dh        ; q. end of line?
  2755.         je        nxtop80            ; a. yes .. scan no more
  2756.  
  2757.         cmp     byte ptr [bx], '/'      ; q. slash?
  2758.         je        nxtop50            ; a. yes .. return next char
  2759.  
  2760.         cmp     byte ptr [bx], ' '      ; q. whitespace?
  2761.         jna     nxtop20            ; a. yes .. skip whitespace
  2762.  
  2763. ; skip non-white space
  2764.  
  2765. nxtop10:    inc     bx                ; bx -> next char
  2766.  
  2767.         cmp     byte ptr [bx], 0dh        ; q. end of line?
  2768.         je        nxtop80            ; a. yes .. scan no more
  2769.  
  2770.         cmp     byte ptr [bx], '/'      ; q. slash?
  2771.         je        nxtop50            ; a. yes .. return next char
  2772.  
  2773.         cmp     byte ptr [bx], ' '      ; q. whitespace?
  2774.         ja        nxtop10            ; a. no .. skip it
  2775.  
  2776. nxtop20:    inc     bx                ; bx -> next char
  2777.  
  2778.         cmp     byte ptr [bx], 0dh        ; q. end of line?
  2779.         je        nxtop80            ; a. yes .. scan no more
  2780.  
  2781.         cmp     byte ptr [bx], '/'      ; q. slash?
  2782.         je        nxtop50            ; a. yes .. return next char
  2783.  
  2784.         cmp     byte ptr [bx], ' '      ; q. whitespace?
  2785.         jna     nxtop20            ; a. yes .. get next char
  2786.         jmp     short nxtop60        ; else .. return char
  2787.  
  2788. nxtop50:    inc     bx                ; bx -> byte after slash
  2789.  
  2790. nxtop60:    mov     al, [bx]            ; al = byte of operand
  2791.  
  2792.         cmp     al, 'a'                 ; q. char >= lower case a?
  2793.         jna     nxtop90            ; a. no .. return as is
  2794.  
  2795.         cmp     al, 'z'                 ; q. char <= lower case z?
  2796.         jnb     nxtop90            ; a. no .. return as is
  2797.  
  2798.         and     al, not 20h         ; xlate to upper case
  2799.         jmp     short nxtop90        ; return to caller, ok
  2800.  
  2801. nxtop80:    stc                 ; carry = end of line
  2802.         ret                 ; return to caller
  2803.  
  2804. nxtop90:    clc                 ; no carry = al contains char
  2805.         ret                 ; return to caller
  2806. nxtop        endp
  2807.  
  2808. ; ----------------------------------------------------------------
  2809. ;   Process the init command;    bx -> next char to process
  2810. ;                di =  B0BFh if spooler is active
  2811. ; ----------------------------------------------------------------
  2812.  
  2813. init        proc
  2814.  
  2815.         cmp     di, 0B0BFh            ; q. spooler already active?
  2816.         jne     init05            ; a. no .. ok to INIT.
  2817.  
  2818.         mov     dx, offset upalrdy        ; dx -> up already message
  2819.         xor     al, al            ; al = no help needed
  2820.         call    die             ; .. die .. with honor
  2821.  
  2822. init05:     push    es                ; save es
  2823.         mov     es, ds:[002ch]        ; es -> environment
  2824.         mov     ah, 49h            ; ax = release it
  2825.         int     21h             ; .. tell dos to make it so
  2826.  
  2827.         push    bx                ; save register
  2828.         mov     ah, 34h            ; ah = get DOS busy flg addr
  2829.         int     21h             ; call DOS
  2830.  
  2831.         mov     word ptr dos_busy, bx   ; save offset
  2832.         mov     word ptr dos_busy+2, es ; ..and segment of busy flag
  2833.         pop     bx                ; restore registers
  2834.         pop     es                ;
  2835.  
  2836. init10:     call    nxtop            ; q. any more options?
  2837.         jc        init50            ; a. no .. process options
  2838.  
  2839.         cmp     al, 'C'                 ; q. conventional?
  2840.         jne     init15            ; a. no .. try next
  2841.  
  2842.         call    getmemz            ; get memory size
  2843.         jmp     init10            ; .. get next operand
  2844.  
  2845. init15:     cmp     al, 'D'                 ; q. disk?
  2846.         jne     init20            ; a. no .. try printer
  2847.  
  2848.         call    getfile            ; get spooler file
  2849.         jmp     init10            ; .. get next operand
  2850.  
  2851. init20:     mov     cl, al            ; prepare to test for printer
  2852.         sub     cl, '1'                 ; .. set to 0, 1 or 2 ..
  2853.  
  2854.         cmp     cl, 2            ; q. lpt1, 2 or 3?
  2855.         ja        init30            ; a. no .. error
  2856.  
  2857.         mov     al, 1            ; al = bit
  2858.         shl     al, cl            ; .. shift to proper position
  2859.         or        pcsflg, al            ; .. turn on bit for printer
  2860.         jmp     init10            ; .. get the next operand
  2861.  
  2862. init30:     mov     invopndcd, al        ; save invalid operand
  2863.         mov     dx, offset invopnd        ; dx -> error message
  2864.         mov     al, 1            ; .. give 'em help
  2865.         call    die             ; tell the operator, then die
  2866.  
  2867. init50:     test    pcsflg, pcsflgsx        ; q. any spool type set?
  2868.         jnz     init60            ; a. yes .. setup mem, etc
  2869.  
  2870.         or        pcsflg, pcsflgcm        ; else .. setup for conventional
  2871.         lea     bx, char_C            ; bx -> 'C' for conv spool
  2872.  
  2873.         mov     ax, memqsize        ; ax = k bytes for queue
  2874.         shl     ax, 1            ; ax = nbr of que records
  2875.         mov     memqsize, ax        ; set up max record cnt
  2876.  
  2877.         call    initq            ; initialize queue rtn
  2878.  
  2879. init60:     call    setmem            ; setup mem, buffer ptr's, etc
  2880.  
  2881.         call    scrinit            ; init the screen buffer
  2882.  
  2883.         ret                 ; return to caller
  2884.  
  2885. init        endp
  2886.  
  2887. ; ----------------------------------------------------------------
  2888. ;   Process the pause command;    bx -> next char to process
  2889. ;                di =  bobfh if spooler is active
  2890. ; ----------------------------------------------------------------
  2891.  
  2892. pause        proc
  2893.         cmp     di, 0B0BFh            ; q. spooler active?
  2894.         je        pause05            ; a. yes.. continue
  2895.  
  2896.         mov     dx, offset notup        ; dx -> not up message
  2897.         call    die
  2898.  
  2899. pause05:    xor     dx, dx            ; assume printer 0
  2900.  
  2901.         call    nxtop            ; find the next operator
  2902.         jc        pause10            ; if end of line..
  2903.  
  2904.         cmp     byte ptr [bx]-1, '/'    ; q. switch?
  2905.         jne     pause10            ; a. no .. must be comment
  2906.  
  2907.         sub     al, '1'                 ; al = adjust as printer nbr
  2908.  
  2909.         cmp     al, 2            ; q. printer number?
  2910.         ja        pause10            ; a. no.. part of comment
  2911.  
  2912.         mov     dl, al            ; dx = new printer number
  2913.  
  2914.         call    nxtop            ; bx -> next operator
  2915.  
  2916. pause10:    mov     si, bx            ; si -> comment, if any
  2917.         xor     cx, cx            ; cx = char counter
  2918.  
  2919. pause15:    cmp     byte ptr [bx], 0dh        ; q. end of line?
  2920.         je        pause18            ; a. yes .. make it asciiz
  2921.  
  2922.         inc     bx                ; bx -> next char
  2923.         inc     cx                ; cx = char counter
  2924.         jmp     pause15            ; .. look at next char
  2925.  
  2926. pause18:    mov     byte ptr [bx], 0        ; ASCIIZ the string
  2927.  
  2928.         mov     ah, 0c0h            ; ah = get control block
  2929.         int     17h             ; es:bx = control block
  2930.  
  2931.         cmp     es:[bx].lpstatus, 0feh  ; q. spooled?
  2932.         jb        pause20            ; a. yes.. process pause
  2933.  
  2934.         mov     dx, offset notspld        ; dx -> not spooled message
  2935.         mov     al, 1            ; al = give help
  2936.         call    die             ; die gracefully
  2937.  
  2938. pause20:    mov     ah, 0c1h            ; ah = build control record
  2939.         int     17h             ; .. ask spooler to do it
  2940.  
  2941.         mov     dx, offset pauseok        ; ds:dx -> completion msg
  2942.         xor     al, al            ; al = no help
  2943.         call    die             ; Message & die
  2944. pause        endp
  2945.  
  2946.  
  2947.  
  2948. ; ----------------------------------------------------------------
  2949. ;   Process the formfeed command;  bx -> next char to process
  2950. ; ----------------------------------------------------------------
  2951.  
  2952. formfeed    proc
  2953.  
  2954.         xor     dx, dx            ; assume printer 0
  2955.  
  2956.         call    nxtop            ; find the next operator
  2957.         jc        formfd10            ; if end of line.. send ff
  2958.  
  2959.  
  2960.         sub     al, '1'                 ; al = adjust as printer nbr
  2961.  
  2962.         cmp     al, 2            ; q. printer number?
  2963.         ja        formfd10            ; a. no.. ignore it
  2964.  
  2965.         mov     dl, al            ; dx = printer number
  2966.  
  2967. formfd10:   mov     ax, 000ch            ; ax = write a formfeed
  2968.         int     17h             ; .. ask spooler to do it
  2969.  
  2970.         cmp     ah, 1            ; q. print ok?
  2971.         je        formfd10            ; a. no .. try again
  2972.  
  2973.         mov     dx, offset ffok        ; ds:dx -> completion msg
  2974.         xor     al, al            ; al = no help
  2975.         call    die             ; Message & die
  2976. formfeed    endp
  2977.  
  2978.  
  2979.  
  2980. ; ----------------------------------------------------------------------
  2981. ;   Process the uninstall command;  bx -> next char to process
  2982. ;                    di =  b0bfh if spooler is active
  2983. ;                    si =  segment of active spooler
  2984. ; ----------------------------------------------------------------------
  2985.  
  2986. uninstall   proc
  2987.         cmp     di, 0b0bfh            ; q. spooler active?
  2988.         je        unins05            ; a. yes .. continue
  2989.  
  2990.         mov     dx, offset    notup        ; dx -> message
  2991.         jmp     die             ; .. die now, sucker
  2992.  
  2993. unins05:    call    needwait            ; q. need to wait?
  2994.         jnc     unins09            ; a. no .. continue
  2995.  
  2996.         lea     dx, waitmsg         ; dx -> wait message
  2997.         mov     ah, 9            ; ah = print ASCII$ message
  2998.         int     21h             ; print message
  2999.  
  3000. unins08:    call    needwait            ; q. need to wait more?
  3001.         jnc     unins09            ; a. no .. continue
  3002.  
  3003.         mov     ah, 0bh            ; ah = check keyboard status
  3004.         int     21h             ; call DOS
  3005.  
  3006.         cmp     al, 0ffh            ; q. character waiting?
  3007.         jne     unins08            ; a. no .. continue waiting
  3008.  
  3009.         mov     ah, 08h            ; ah = get char
  3010.         int     21h             ; al = char
  3011.  
  3012.         cmp     al, 1bh            ; q. escape?
  3013.         jne     unins09            ; a. no .. continu
  3014.  
  3015.         mov     dx, offset uninscan     ; dx -> cancelled message
  3016.         jmp     short unins90        ; .. and finish now
  3017.  
  3018. unins09:    mov     al, 0c7h            ; ah = close queue request
  3019.         int     17h             ; call resident PCSPOOL
  3020.  
  3021.         mov     ds, si            ; ds -> spooler's memory
  3022.         mov     es, si            ; es -> same
  3023.  
  3024.         mov     di, intblk1         ; bx -> first interrupt blk
  3025.         mov     cx, intblkcnt        ; cx = number of ints used
  3026.  
  3027. unins10:    mov     ah, 35h            ; ah = get interrupt
  3028.         mov     al, [di+12]         ; al = interrupt number
  3029.         int     21h             ; es:bx -> interrupt
  3030.  
  3031.         mov     ax, es            ; ax = int segment
  3032.         cmp     ax, si            ; q. our segment?
  3033.         jne     unins80            ; a. no .. can't uninstall
  3034.  
  3035.         add     di, intblklen        ; si -> next block
  3036.         loop    unins10            ; .. check next block
  3037.  
  3038.         mov     di, intblk1         ; si -> first interrupt blk
  3039.         mov     cx, intblkcnt        ; cx = number of ints used
  3040.  
  3041.         cli                 ; no ints ..
  3042. unins20:    lds     dx, dword ptr es:[di]   ; ds:dx -> old interrupt rtn
  3043.         mov     ah, 25h            ; ah = set interrupt
  3044.         mov     al, es:[di+12]        ; al = int to set
  3045.         int     21h             ; .. set the interrupt
  3046.  
  3047.         add     di, intblklen        ; si -> next block
  3048.         loop    unins20            ; .. reset next interrupt
  3049.         sti                 ; .. allow interrupts again
  3050.  
  3051.         mov     ah, 49h            ; ah = free (spooler's) mem
  3052.         int     21h             ; .. do it
  3053.  
  3054.         mov     dx, offset freeok        ; dx -> freed ok.
  3055.         jmp     short unins90        ; .. end the job, painlessly
  3056.  
  3057. unins80:    mov     dx, offset cantfree     ; dx -> can't free message
  3058.  
  3059. unins90:    xor     al, al            ; al = no help
  3060.  
  3061.         push    cs                ; restore our ..
  3062.         pop     ds                ; ..own segment
  3063.  
  3064.         call    die             ; die .. hard & fast
  3065.  
  3066. uninstall   endp
  3067.  
  3068.  
  3069.  
  3070. ; ----------------------------------------------------------------------
  3071. ;   This routine checks for the need to wait for the spooler
  3072. ;   to queuese(?)
  3073. ;
  3074. ;   Returns
  3075. ;    carry = wait needed
  3076. ;
  3077. ; ----------------------------------------------------------------------
  3078.  
  3079. needwait    proc
  3080.         push    cx                ; save registers
  3081.         push    dx
  3082.  
  3083.         mov     cx, 3            ; cx = loop counter
  3084.         xor     dx, dx            ; dx = lpt1:
  3085.  
  3086. needwa10:   mov     ax, 0c600h            ; ah = check wait condition
  3087.         int     17h             ; call printer handler
  3088.  
  3089.         or        al, al            ; q. need to wait?
  3090.         jz        needwa80            ; a. no .. check next one
  3091.  
  3092. needwa20:   stc                 ; wait needed
  3093.         jmp     short needwa90        ; ..and exit w/carry set
  3094.  
  3095. needwa80:   inc     dx                ; dx = next printer
  3096.         loop    needwa10            ; .. loop again
  3097.  
  3098.         clc                 ; no wait needed
  3099.  
  3100. needwa90:   pop     dx                ; restore registers
  3101.         pop     cx
  3102.         ret                 ; return to caller
  3103.  
  3104. needwait    endp
  3105.  
  3106.  
  3107. ; ----------------------------------------------------------------------
  3108. ;   Get memory spool size req;    bx -> "C" of operand
  3109. ;
  3110. ;               return:    queuez     is set to size
  3111. ;                pcsflgcm is set on
  3112. ; ----------------------------------------------------------------------
  3113.  
  3114. getmemz     proc
  3115.  
  3116.         test    pcsflg, pcsflgsx        ; q. any spool type set?
  3117.         jz        getmemz10            ; a. no .. setup memory size
  3118.  
  3119.         mov     dx, offset splalrdy     ; dx -> spooled type sel'd
  3120.         mov     al, 1            ; al = give 'em help
  3121.         call    die             ; play taps
  3122.  
  3123. getmemz10:  or        pcsflg, pcsflgcm        ; show conventional chosen
  3124.         mov     ax, memqsize        ; ax = default queue size
  3125.  
  3126.         cmp     byte ptr [bx+1], ' '    ; q. whitespace?
  3127.         jna     getmemz80            ; a. yes .. use default
  3128.  
  3129.         cmp     byte ptr [bx+1], '/'    ; q. slash?
  3130.         je        getmemz80            ; a. yes .. use default
  3131.  
  3132.         xor     ax, ax            ; clear ax for value calc
  3133.         mov     cx, [bx+1]            ; cx = spool size chars
  3134.         sub     cx, '00'                ; convert to binary
  3135.  
  3136.         cmp     cl, 9            ; q. first char value valid?
  3137.         ja        getmemz70            ; a. no .. error
  3138.  
  3139.         mov     al, cl            ; al = first value
  3140.  
  3141.         cmp     ch, 9            ; q. 2nd digit valid?
  3142.         ja        getmemz80            ; a. no .. continue
  3143.  
  3144.         mov     cl, 10            ; ch = multiplier
  3145.         mul     cl                ; ax = ax * 10
  3146.         mov     cl, ch            ; setup cl w/2nd digit
  3147.         xor     ch, ch            ; ch = 0
  3148.         add     ax, cx            ; ax = total memory size
  3149.  
  3150.         cmp     ax, 64            ; q. valid value?
  3151.         ja        getmemz70            ; a. no .. error
  3152.  
  3153.         cmp     ax, 4            ; q. lower bounds ok?
  3154.         jnb     getmemz80            ; a. no .. error
  3155.  
  3156. getmemz70:  mov     dx, offset invmemz        ; dx -> invalid memory size
  3157.         mov     al, 1            ; al = give 'em help
  3158.         call    die             ; .. ice 'im
  3159.  
  3160. getmemz80:  shl     ax, 1            ; ax = nbr of que records
  3161.         mov     memqsize, ax        ; set up max record cnt
  3162.  
  3163. getmemz90:  call    initq            ; return to caller
  3164.         ret
  3165.  
  3166. getmemz     endp
  3167.  
  3168.  
  3169.  
  3170. ; ----------------------------------------------------------------
  3171. ;   Get the spool file name;    bx -> "D" of operand
  3172. ; ----------------------------------------------------------------
  3173.  
  3174. getfile     proc
  3175.  
  3176.         test    pcsflg, pcsflgsx        ; q. any spool type set?
  3177.         jz        getfile10            ; a. no .. setup filename
  3178.  
  3179.         mov     dx, offset splalrdy     ; dx -> spooled type sel'd
  3180.         mov     al, 1            ; al = help the helpless
  3181.         call    die             ; play taps
  3182.  
  3183. getfile10:  or        pcsflg, pcsflgdk        ; show disk spooling chosen
  3184.  
  3185.         cmp     byte ptr [bx+1], ' '    ; q. whitespace?
  3186.         jna     getfile50            ; a. yes .. use default
  3187.  
  3188.         cmp     byte ptr [bx+1], '/'    ; q. slash?
  3189.         je        getfile50            ; a. yes .. use default
  3190.  
  3191.         lea     si, [bx+1]            ; si -> next char in cmd
  3192.         mov     di, offset qfile        ; di -> filename work area
  3193.         cld                 ; .. move & scan up
  3194.  
  3195. getfile20:  movsb                ; move a byte to work area
  3196.  
  3197.         cmp     byte ptr [si], ' '      ; q. whitespace?
  3198.         jna     getfile40            ; a. yes .. end of parm
  3199.  
  3200.         cmp     byte ptr [si], '/'      ; q. switch?
  3201.         jne     getfile20            ; a. yes .. end of parm
  3202.  
  3203. getfile40:  cmp     byte ptr [di-1], ':'    ; q. last char a colon?
  3204.         je        getfile45            ; a. yes.. no path wanted
  3205.  
  3206.         cmp     byte ptr [di-1], '\'    ; q. last char backslash?
  3207.         je        getfile45            ; a. yes.. path ok.
  3208.  
  3209.         mov     byte ptr [di], '\'      ; end path w/backslash
  3210.         inc     di                ; di -> next char
  3211.  
  3212. getfile45:  mov     byte ptr [di], 0        ; assure nul after path
  3213.  
  3214. getfile50:  mov     di, offset qfile        ; di -> filename work area
  3215.         mov     cx, 65            ; cx = length to scan
  3216.         xor     al, al            ; .. for a nul
  3217.       repnz scasb
  3218.         dec     di                ; di -> first nul
  3219.  
  3220.         mov     si, offset qfilenm        ; si -> queue file name
  3221.  
  3222. getfile55:  movsb                ; move a char
  3223.  
  3224.         cmp     byte ptr [si-1], 0        ; q. end of move?
  3225.         jne     getfile55            ; a. no .. continue move
  3226.  
  3227. getfile90:  call    initq            ; initialize the queue rtn
  3228.         ret                 ; return to caller
  3229.  
  3230. getfile     endp
  3231.  
  3232.  
  3233.  
  3234. ; ----------------------------------------------------------------
  3235. ;   setup all memory addresses;     qrtnlen = length of queue rtn
  3236. ;
  3237. ;                Return: Memory pointers setup:
  3238. ;                    - live prt memory puddles
  3239. ;                    - live prt queue rec buffers
  3240. ;                    - interrupt stacks
  3241. ;                    - single dequeue record
  3242. ;
  3243. ;   Note: The conventional memory buffer cannot be setup here because
  3244. ;      this mem must first be freed before the buffer is allocated.
  3245. ;      The qrtn pointer is not set up in this routine. It is set up
  3246. ;      when the routine is moved to the final qrtn area.
  3247. ;
  3248. ;      A "live prt" is a printer whose address is noted in the
  3249. ;      printer vector at absolute address 40:8.
  3250. ; ----------------------------------------------------------------
  3251.  
  3252. setmem        proc
  3253.         push    ax                ; save caller regs
  3254.         push    bx
  3255.         push    cx
  3256.         push    dx
  3257.         push    si
  3258.         push    di
  3259.         push    es
  3260.  
  3261.         mov     si, nxtavail        ; si -> next available byte
  3262.         add     si, qrtnlen         ; si -> byte after handler
  3263.         mov     startclr, si        ; save start of allocateds
  3264.  
  3265.         mov     bx, offset qhlpt1        ; bx -> first printer
  3266.         mov     cx, 3            ; max printers
  3267.         xor     ax, ax            ; ax = 0
  3268.         mov     es, ax            ; es -> low memory
  3269.  
  3270. setmem10:   mov     di, [bx].lpprtnbr        ; dx = printer number
  3271.         shl     di, 1            ; dx = dx * 2
  3272.  
  3273.         mov     dx, word ptr es:[di]+408h    ; dx = base prt addr
  3274.  
  3275.         or        dx, dx            ; q. any more printers?
  3276.         jz        setmem15            ; a. no .. exit
  3277.  
  3278.         cmp     dx, 100h            ; q. local printer
  3279.         jb        setmem13            ; a. no .. next prt
  3280.  
  3281.         inc     dx                ; dx = status port
  3282.         mov     [bx].lpstatport, dx     ; save for later
  3283.  
  3284.         mov     dx, si            ; dx = next available byte
  3285.         push    cx                ; save cx
  3286.         mov     cl, 4            ; cl = shift amt
  3287.         shr     dx, cl            ; dx = dx / 16
  3288.         pop     cx                ; .. restore cx
  3289.  
  3290.         mov     ax, ds            ; ax = pgm segment
  3291.         add     ax, dx            ; ax = puddle seg
  3292.         mov     word ptr [bx].lpdqbuf+2, ax ; save puddle segment
  3293.  
  3294.         add     si, lpt_dqsz        ; si -> next byte
  3295.         mov     [bx].lpwrkqr, si        ; save queue record address
  3296.  
  3297.         push    cx                ; save register
  3298.         mov     cx, [bx].lpprtnbr        ; cx = printer nbr
  3299.         mov     al, 1            ; al = a bit
  3300.         shl     al, cl            ; al = bit mask
  3301.         pop     cx                ; restore cx
  3302.  
  3303.         test    pcsflg, al            ; q. spool this printer?
  3304.         jz        setmem11            ; a. no .. mark not spooled
  3305.  
  3306.         mov     [bx].lpstatus, 0        ; setup status as spooled
  3307.         jmp     short setmem12        ; ..and continue
  3308.  
  3309. setmem11:   mov     [bx].lpstatus, 0ffh     ; status is not spooled
  3310.  
  3311. setmem12:   add     si, qrlen            ; si -> next available byte
  3312.  
  3313. setmem13:   add     bx, lplen            ; bx -> next lp routine
  3314.         loop    setmem10            ; set up next printer
  3315.  
  3316. setmem15:   mov     bx, intblk1 + 8        ; bx -> first new ss
  3317.         mov     cx, intblkcnt        ; cx -> number of new ss
  3318.  
  3319. setmem20:   mov     [bx], si            ; save pointer to stack
  3320.         mov     [bx]+2, ds            ; .. and segment
  3321.  
  3322.         add     si, intstksz        ; si -> next stack area
  3323.         add     bx, intblklen        ; bx -> next pointer
  3324.         loop    setmem20            ; .. build next stack
  3325.  
  3326.         mov     dqrec, si            ; si -> dequeue record
  3327.         add     si, qrlen            ; si -> next available byte
  3328.  
  3329.         mov     nxtavail, si        ; save new next avail ptr
  3330.  
  3331.         sub     si, startclr        ; si = length to clear
  3332.         mov     clrlen, si            ; save for later
  3333.  
  3334.         pop     es                ; restore caller regs
  3335.         pop     di
  3336.         pop     si
  3337.         pop     dx
  3338.         pop     cx
  3339.         pop     bx
  3340.         pop     ax
  3341.         ret
  3342. setmem        endp
  3343.  
  3344. rsetints    proc
  3345. rsetints    endp
  3346.  
  3347.  
  3348.  
  3349. ; ----------------------------------------------------------------------
  3350. ;   Init the queue handler;
  3351. ; ----------------------------------------------------------------------
  3352.  
  3353. initq        proc
  3354.  
  3355.         call    findrtn            ; find the queue routine
  3356.  
  3357.         mov     word ptr qrtn, 3        ; setup the offset for the call
  3358.         xor     ax, ax            ; ax = 0 = init spool
  3359.         call    dword ptr qrtn        ; call the handler
  3360.         jnc     initq90            ; .. return if all ok
  3361.  
  3362.         mov     dx, offset cantinitq    ; dx -> init q error
  3363.         xor     al, al            ; al = no help
  3364.         call    die             ; ..die in flames
  3365.  
  3366. initq90:    ret
  3367.  
  3368. initq        endp
  3369.  
  3370.  
  3371.  
  3372. ; ----------------------------------------------------------------------
  3373. ;   Find the queue handler;    bx -> first char of switch
  3374. ;
  3375. ;            Return: qrtnseg = segment of routine
  3376. ;                qrtnlen = length of routine (in bytes)
  3377. ; ----------------------------------------------------------------------
  3378.  
  3379. findrtn     proc                ; find abs address of rtn
  3380.  
  3381.         mov     al, [bx]            ; al = first char of switch
  3382.         and     al, NOT 20h         ; upper case it.
  3383.  
  3384.         mov     si, offset mainlen        ; si -> start of 1st qrtn
  3385.  
  3386. findrtn10:  cmp     [si+2], al            ; q. this the routine?
  3387.         je        findrtn50            ; a. yes .. process the addr
  3388.  
  3389.         add     si, [si]            ; si -> next routine
  3390.         jmp     findrtn10            ; .. find the next routine
  3391.  
  3392. findrtn50:  mov     dx, [si]            ; dx = length of routine
  3393.         mov     qrtnlen, dx         ; .. save the routine length
  3394.  
  3395.         mov     cl, 4            ; cl = shift amt
  3396.         shr     si, cl            ; .. div by 16
  3397.         mov     ax, cs            ; ax = current segment
  3398.         add     ax, si            ; ax = driver's segment
  3399.  
  3400.         mov     qrtnseg, ax         ; save segment of routine
  3401.  
  3402.         ret                 ; return to caller
  3403.  
  3404.  
  3405. findrtn     endp
  3406.  
  3407.  
  3408. ; ----------------------------------------------------------------------
  3409. ;   initiailize screen stuff;
  3410. ; ----------------------------------------------------------------------
  3411.  
  3412. scrinit     proc                ; init the screen buffer
  3413.         push    ax                ; save registers
  3414.         push    bx
  3415.         push    cx
  3416.         push    dx
  3417.         push    si
  3418.         push    di
  3419.  
  3420.         mov     ah, 0fh            ; get current display mode
  3421.         int     10h             ; .. ask BIOS
  3422.  
  3423.         cmp     al, 3            ; q. Color mode?
  3424.         jne     scrinit10            ; a. No .. leave in mono
  3425.  
  3426.         mov     ourattr, colattr        ; else .. assume color attr
  3427.  
  3428. scrinit10:  mov     di, offset scrbufend    ; di -> end of buffer
  3429.         mov     si, offset scrbufcon    ; si -> end of constants
  3430.  
  3431.         mov     al, ourattr         ; al = attribute to use
  3432.         mov     cx, 400            ; .. chars to move
  3433.         std                 ; .. in backward direction
  3434.  
  3435. scrinit20:  stosb                ; save an attribute
  3436.         movsb                ; .. move constant data
  3437.         loop    scrinit20            ; .. until all moved
  3438.  
  3439.         cld                 ; reset direction
  3440.  
  3441.         pop     di                ; restore regs
  3442.         pop     si
  3443.         pop     dx
  3444.         pop     cx
  3445.         pop     bx
  3446.         pop     ax
  3447.         ret                 ; return to caller
  3448.  
  3449. scrinit     endp
  3450.  
  3451.  
  3452. ; ----------------------------------------------------------------------
  3453. ;   terminate w/help message; dx -> intermediate message
  3454. ;                  al not zero if help wanted
  3455. ; ----------------------------------------------------------------------
  3456.  
  3457. die        proc
  3458.         push    ax                ; save help request
  3459.  
  3460.         mov     ah, 9            ; ah = print ASCII$ message
  3461.         int     21h             ; print the error message
  3462.  
  3463.         pop     ax                ; restore help request
  3464.         or        al, al            ; q. help wanted?
  3465.         jz        die10            ; a. no .. exit
  3466.  
  3467.         mov     dx, offset help        ; dx -> help message
  3468.         mov     ah, 9            ; ah = print ASCII$ message
  3469.         int     21h             ; ... print the help
  3470.  
  3471. die10:        mov     ax, 4c01h            ; die w/some notice
  3472.         int     21h             ; ... return to dos
  3473. die        endp
  3474.  
  3475.         align   16                ; line up on paragraph
  3476. mainlen     label   byte            ; .. length of main
  3477.  
  3478. mainline    ends
  3479.  
  3480. ; ----------------------------------------------------------------------
  3481. ;   This is the spool interface module.  This supports the device
  3482. ;   independence for the spooling media.
  3483. ;
  3484. ;
  3485. ;   General Returns
  3486. ;    carry flag = error
  3487. ;
  3488. ;
  3489. ;   Initialize Spool
  3490. ;    ax = 00h, function code
  3491. ;
  3492. ;
  3493. ;   Read Queue Record
  3494. ;    ax = 01h, fuction code
  3495. ;    cx = record number
  3496. ;    dx -> buffer
  3497. ;
  3498. ;
  3499. ;   Write Queue Record
  3500. ;    ax = 02h, fuction code
  3501. ;    cx = record number
  3502. ;    dx -> buffer
  3503. ;
  3504. ;
  3505. ;   Read Queue Pointer
  3506. ;    ax = 03h, fuction code
  3507. ;    cx = record number
  3508. ;    dx -> buffer
  3509. ;
  3510. ;
  3511. ;   Write Queue Pointer
  3512. ;    ax = 04h, fuction code
  3513. ;    cx = record number
  3514. ;    dx -> buffer
  3515. ;
  3516. ;
  3517. ;   Allocate a Queue Record
  3518. ;    ax = 05h, function code
  3519. ;
  3520. ;     Returns
  3521. ;    cx = record number
  3522. ;
  3523. ;
  3524. ;   Commit File
  3525. ;    ax = 06h, function code
  3526. ;
  3527. ;
  3528. ;   Close Spool
  3529. ;    ax = 07h, function code
  3530. ;
  3531. ; ----------------------------------------------------------------------
  3532.  
  3533. diskq        segment para public 'code'      ; disk spool routines
  3534.         assume  cs:diskq            ; called via far call
  3535.  
  3536. disklen     dw        diskend            ; length of module
  3537.         db        'D'                     ; module identifier
  3538.  
  3539.         jmp     short disk            ; jump to disk routine
  3540.  
  3541. diskhandle  dw        0                ; disk handle
  3542. closeq        db        0                ; close queue needed
  3543.  
  3544. disktbl     label   byte            ; function table
  3545.         dw        disk1            ; Initialize Spool
  3546.         dw        disk2            ; Read Queue Record
  3547.         dw        disk3            ; Write Queue Record
  3548.         dw        disk4            ; Read Queue Record
  3549.         dw        disk5            ; Write Queue Record
  3550.         dw        disk6            ; Allocate a Queue Record
  3551.         dw        disk7            ; Commit File
  3552.         dw        disk8            ; Close Spool
  3553.  
  3554.  
  3555. disk        proc    far             ; disk spool interface
  3556.         push    si                ; save registers
  3557.         push    bx
  3558.         push    dx
  3559.  
  3560.         shl     ax,1            ; ax = offset of routine
  3561.         mov     si, ax            ; si = ...
  3562.  
  3563.         mov     ah, 62h            ; get user's psp
  3564.         int     21h             ; call DOS
  3565.  
  3566.         push    bx                ; save old psp
  3567.  
  3568.         mov     ah, 50h            ; ah = store psp
  3569.         mov     bx, ds            ; bx -> pcs's psp
  3570.         int     21h             ; call DOS
  3571.  
  3572.         push    cx                ; save last register
  3573.         mov     bx, cs:diskhandle        ; bx = file handle
  3574.         jmp     word ptr cs:disktbl[si] ; goto proper entry point
  3575.  
  3576.  
  3577.                         ; --------------------------
  3578. disk1        label   byte            ; Initialize Spool
  3579.         mov     ax, 3d02h            ; ax = open function code
  3580.         lea     dx, qfile            ; dx -> file name
  3581.         int     21h             ; q. open ok?
  3582.         jnc     disk150            ; a. yes .. read header
  3583.  
  3584. disk110:    mov     ah, 3ch            ; ah = create function code
  3585.         lea     dx, qfile            ; dx -> file name
  3586.         mov     cx, 0            ; cx = normal file attribute
  3587.         int     21h             ; try to create the spool
  3588.         jc        disk130            ; die if error
  3589.         mov     cs:diskhandle, ax        ; save disk handle
  3590.         mov     bx, ax            ; bx = spool file handle
  3591.  
  3592.         mov     ah, 40h            ; ax = write file function
  3593.         mov     cx, qhlen            ; cx = length of queue hdr
  3594.         mov     dx, offset qhfirstf     ; dx -> read buffer
  3595.         int     21h             ; call DOS to write record
  3596.         jc        disk130            ; die if error
  3597.         jmp     disk700            ; else.. commit the file
  3598. disk130:    jmp     disk9            ; ..return with status
  3599.  
  3600. disk150:    mov     cs:diskhandle, ax        ; save disk handle
  3601.         mov     bx, ax            ; bx = spool file's handle
  3602.         xor     cx, cx            ; cx = record 0
  3603.         call    diskrec            ; position file for que hdr
  3604.  
  3605.         mov     ah, 3fh            ; ax = read file function
  3606.         mov     cx, 2            ; cx = get queue signature
  3607.         mov     dx, offset qheader        ; dx -> read buffer
  3608.         int     21h             ; q. read header ok?
  3609.         jc        disk165            ; a. no .. close and create
  3610.  
  3611.         cmp     qhvalid, 0b0bfh        ; q. valid queue header?
  3612.         jne     disk165            ; a. yes .. continue
  3613.  
  3614. disk160:    mov     ah, 3fh            ; ax = read file function
  3615.         mov     cx, qhlen - 2        ; cx = length of queue hdr
  3616.         mov     dx, offset qhfirstf     ; dx -> read buffer
  3617.         int     21h             ; q. read rest of header ok?
  3618.         jnc     disk170            ; a. yes .. continue
  3619.  
  3620. disk165:    mov     ah, 3eh            ; ah = close file
  3621.         int     21h             ; .. close the file
  3622.         jmp     disk110            ; else .. error return
  3623.  
  3624. disk170:    mov     ax, 4202h            ; ax = lseek to end of file
  3625.         xor     cx, cx            ; cx = 0
  3626.         mov     dx, cx            ; cx:dx = 0, lseek offset
  3627.         int     21h             ; call DOS
  3628.  
  3629.         mov     cx, qrlen            ; cx = queue record length
  3630.         div     cx                ; ax = nbr of records in que
  3631.         mov     nextqrnbr, ax        ; save next nbr for allocate
  3632.         jmp     short disk9         ; ..and return to caller
  3633.  
  3634.  
  3635.                         ; --------------------------
  3636. disk2        label   byte            ; Read Queue Record
  3637.         call    diskrec            ; q. seek to the right rec?
  3638.         jc        disk9            ; a. no .. error return
  3639.  
  3640. disk210:    mov     ah, 3fh            ; ah = read file function
  3641.         jmp     short disk310        ; ..continue w/common code
  3642.  
  3643.  
  3644.                         ; --------------------------
  3645. disk3        label   byte            ; Write Queue Record
  3646.         call    diskrec            ; q. seek to the right rec?
  3647.         jc        disk9            ; a. no .. error return
  3648.  
  3649.         mov     ah, 40h            ; ah = write file function
  3650. disk310:    mov     cx, qrlen            ; cx = length of queue rec
  3651.         int     21h             ; call DOS
  3652.         jc        disk9            ; ..if error, return immed.
  3653.  
  3654.         cmp     cs:closeq, 1        ; q. need to close?
  3655.         je        disk700            ; a. yes .. do a commit file
  3656.  
  3657.         clc                 ; clear carry
  3658.         jmp     short disk9         ; ..and rtn w/proper status
  3659.  
  3660.  
  3661.                         ; --------------------------
  3662. disk4        label   byte            ; Read Queue Pointer
  3663.         call    diskrec            ; q. seek to the right rec?
  3664.         jc        disk9            ; a. no .. error return
  3665.  
  3666.         mov     ah, 3fh            ; ah = read file function
  3667.         jmp     short disk520        ; ..continue w/common code
  3668.  
  3669.  
  3670.                         ; --------------------------
  3671. disk5        label   byte            ; Write Queue Pointer
  3672.         call    diskrec            ; q. seek to the right rec?
  3673.         jc        disk9            ; a. no .. error return
  3674.  
  3675.         mov     ah, 40h            ; ah = write file function
  3676. disk520:    mov     cx, 2            ; cx = length of queue ptr
  3677.         int     21h             ; call DOS
  3678.         jmp     short disk9         ; ..return with status
  3679.  
  3680.  
  3681.                         ; --------------------------
  3682. disk6        label   byte            ; Allocate a Queue Record
  3683.         mov     cx, nextqrnbr        ; cx = next record nbr
  3684.         inc     nextqrnbr            ; setup next record nbr
  3685.         mov     cs:closeq, 1        ; setup to close next write
  3686.         clc                 ; clear carry flag
  3687.         add     sp, 2            ; skip returning cx
  3688.         jmp     short disk910        ; ..then return w/record nbr
  3689.  
  3690.  
  3691.                         ; --------------------------
  3692. disk7        label   byte            ; Commit File
  3693. disk700:    mov     cs:closeq, 0        ; clear close needed flag
  3694.  
  3695.         mov     ah, 45h            ; ah = duplicate file handle
  3696.         int     21h             ; call DOS
  3697.  
  3698.         mov     bx, ax            ; bx = dup'd file handle
  3699.         jmp     short disk810        ; ..continue w/common code
  3700.  
  3701.  
  3702.                         ; --------------------------
  3703. disk8        label   byte            ; Close Spool
  3704.         xor     cx, cx            ; cx = record 0
  3705.         call    diskrec            ; position file for que hdr
  3706.  
  3707.         mov     qhvalid, 0b0bfh        ; make record valid
  3708.         mov     ah, 40h            ; ax = write file function
  3709.         mov     cx, qhlen            ; cx = length of queue hdr
  3710.         mov     dx, offset qheader        ; dx -> read buffer
  3711.         int     21h             ; q. write header ok?
  3712. ;        jc        disk9            ; a. no .. rtn w/error stat
  3713.  
  3714. disk810:    mov     ah, 3eh            ; ah = close file handle fnc
  3715.         int     21h             ; call DOS
  3716. ;        jmp     disk9            ; ..rtn to caller w/status
  3717.  
  3718.  
  3719.                         ; --------------------------
  3720.                         ; Return to caller w/status
  3721. disk9:        pop     cx                ; restore register
  3722.  
  3723. disk910:    pop     bx                ; bx = user's psp
  3724.         pushf                ; save flags
  3725.         mov     ah, 50h            ; ah = reset user's psp
  3726.         int     21h             ; call DOS
  3727.  
  3728.         popf                ; restore flags
  3729.         pop     dx                ; restore rest of registers
  3730.         pop     bx
  3731.         pop     si
  3732.         ret                 ; rtn w/carry flag as status
  3733.  
  3734. disk        endp
  3735.  
  3736.  
  3737.  
  3738. ; ----------------------------------------------------------------------
  3739. ;   This routine takes the record number and positions the file for the
  3740. ;   next read/write.
  3741. ;
  3742. ;   Entry
  3743. ;    bx = file handle
  3744. ;    cx = record number
  3745. ;
  3746. ;     Returns
  3747. ;    carry flag = error
  3748. ;
  3749. ; ----------------------------------------------------------------------
  3750.  
  3751. diskrec     proc
  3752.         push    cx                ; save registers
  3753.         push    dx
  3754.  
  3755.         mov     ax, qrlen            ; ax = queue record length
  3756.         mul     cx                ; dx:ax = file record offset
  3757.  
  3758.         mov     cx, dx            ; cx = most upper word
  3759.         mov     dx, ax            ; cx:ax = offset in file
  3760.         mov     ax, 4200h            ; ax = lseek function code
  3761.         int     21h             ; call DOS
  3762.  
  3763.         pop     dx                ; restore registers
  3764.         pop     cx
  3765.         ret
  3766. diskrec     endp
  3767.  
  3768.         align   16                ; boundary for next driver
  3769. diskend     label   byte
  3770.  
  3771. diskq        ends
  3772.  
  3773.  
  3774. ; ----------------------------------------------------------------------
  3775. ;   This is the conventional memory spool handler
  3776. ;
  3777. ; ----------------------------------------------------------------------
  3778.  
  3779. convq        segment para public 'code'      ; conventional memory spool
  3780.         assume  cs:convq            ; called via far call
  3781.  
  3782. convlen     dw        convend            ; length of module
  3783.         db        'C'                     ; module identifier
  3784.  
  3785.         jmp     short conv            ; jump to routine
  3786.  
  3787. convtbl     label   byte            ; function table
  3788.         dw        conv1            ; Initialize Spool
  3789.         dw        conv2            ; Read Queue Record
  3790.         dw        conv3            ; Write Queue Record
  3791.         dw        conv4            ; Read Queue Record
  3792.         dw        conv5            ; Write Queue Record
  3793.         dw        conv6            ; Allocate a Queue Record
  3794.         dw        conv7            ; Commit File
  3795.         dw        conv8            ; Close Spool
  3796.  
  3797.  
  3798. conv        proc    far             ; conv spool interface
  3799.         push    si                ; save registers
  3800.         push    bx
  3801.         push    dx
  3802.         push    cx
  3803.  
  3804.         shl     ax,1            ; ax = offset of routine
  3805.         mov     si, ax            ; si -> routine to goto
  3806.         jmp     word ptr cs:convtbl[si] ; goto proper routine
  3807.  
  3808.  
  3809.                         ; --------------------------
  3810. conv1        label   byte            ; Initialize Spool
  3811.         jmp     short conv9         ; ..and return to caller
  3812.  
  3813.  
  3814.                         ; --------------------------
  3815. conv2        label   byte            ; Read Queue Record
  3816.         call    convrec            ; seek to the right "record"
  3817.  
  3818.         mov     cx, qrlen            ; cx = length of queue rec
  3819.         call    convread            ; "read" the record
  3820.         jmp     short conv9         ; ..and return to caller
  3821.  
  3822.  
  3823.                         ; --------------------------
  3824. conv3        label   byte            ; Write Queue Record
  3825.         call    convrec            ; seek to the right "record"
  3826.  
  3827.         mov     cx, qrlen            ; cx = length of queue rec
  3828.         call    convwrite            ; "write" the record
  3829.         jmp     short conv9         ; ..and return to caller
  3830.  
  3831.  
  3832.                         ; --------------------------
  3833. conv4        label   byte            ; Read Queue Pointer
  3834.         call    convrec            ; seek to the right "record"
  3835.  
  3836.         mov     cx, 2            ; cx = length of queue rec
  3837.         call    convread            ; "read" the record
  3838.  
  3839.         jmp     short conv9         ; ..and return to caller
  3840.  
  3841.  
  3842.                         ; --------------------------
  3843. conv5        label   byte            ; Write Queue Pointer
  3844.         call    convrec            ; seek to the right "record"
  3845.  
  3846.         mov     cx, 2            ; cx = length of queue rec
  3847.         call    convwrite            ; "write" the record
  3848.  
  3849.         jmp     short conv9         ; ..and return to caller
  3850.  
  3851.  
  3852.                         ; --------------------------
  3853. conv6        label   byte            ; Allocate a Queue Record
  3854.         mov     cx, nextqrnbr        ; cx = next record nbr
  3855.  
  3856.         cmp     cx, memqsize        ; q. enough records?
  3857.         jae     conv610            ; a. yes .. return w/error
  3858.  
  3859.         inc     nextqrnbr            ; setup next record nbr
  3860.         clc                 ; clear carry flag
  3861.         add     sp, 2            ; skip returning cx
  3862.         jmp     short conv910        ; ..then return w/record nbr
  3863.  
  3864. conv610:    stc                 ; set carry/error flag
  3865.         pop     cx                ; restore registers
  3866.         pop     dx
  3867.         pop     bx
  3868.         pop     si
  3869.         ret                 ; return w/carry flag
  3870.  
  3871.  
  3872.                         ; --------------------------
  3873. conv7        label   byte            ; Commit File
  3874. ;        jmp     conv9            ; ..rtn to caller w/status
  3875.  
  3876.  
  3877.                         ; --------------------------
  3878. conv8        label   byte            ; Close Spool
  3879. ;        jmp     conv9            ; ..rtn to caller w/status
  3880.  
  3881.  
  3882.                         ; --------------------------
  3883.                         ; Return to caller w/status
  3884. conv9:        pop     cx                ; restore registers
  3885. conv910:    pop     dx
  3886.         pop     bx
  3887.         pop     si
  3888.         clc                 ; clear carry flag
  3889.         ret                 ; return w/carry flag
  3890.  
  3891. conv        endp
  3892.  
  3893.  
  3894.  
  3895. ; ----------------------------------------------------------------------
  3896. ;   This routine takes the record number and positions the file for the
  3897. ;   next read/write.
  3898. ;
  3899. ;   Entry
  3900. ;    cx = record number
  3901. ;
  3902. ;     Returns
  3903. ;    bx -> record offset
  3904. ;
  3905. ; ----------------------------------------------------------------------
  3906.  
  3907. convrec     proc
  3908.         push    cx                ; save registers
  3909.         push    dx
  3910.  
  3911.         mov     ax, qrlen            ; ax = queue record length
  3912.         dec     cx                ; cx = zero-based rec number
  3913.         mul     cx                ; dx:ax = file record offset
  3914.  
  3915.         mov     bx, ax            ; bx -> this record
  3916.  
  3917.         pop     dx                ; restore registers
  3918.         pop     cx
  3919.         ret
  3920. convrec     endp
  3921.  
  3922.  
  3923.  
  3924. ; ----------------------------------------------------------------------
  3925. ;   This routine "reads" a record from the spool (conventional memory)
  3926. ;
  3927. ;   Entry
  3928. ;    bx -> spool record
  3929. ;    cx = length
  3930. ;    ds:dx -> caller's record
  3931. ;
  3932. ; ----------------------------------------------------------------------
  3933.  
  3934. convread    proc
  3935.         push    cx                ; save registers
  3936.         push    si
  3937.         push    di
  3938.         push    es
  3939.         push    ds
  3940.  
  3941.         mov     si, bx            ; si -> spool record
  3942.         mov     di, dx            ; di -> user's buffer
  3943.         push    ds                ; make ..
  3944.         pop     es                ; es:di -> user's buffer
  3945.         mov     ds, memqseg         ; ds:si -> spool record
  3946.         cld                 ; .. the right way
  3947.  
  3948.        rep  movsb                ; move the record
  3949.  
  3950.         pop     ds                ; restore registers
  3951.         pop     es
  3952.         pop     di
  3953.         pop     si
  3954.         pop     cx
  3955.         ret
  3956.  
  3957. convread    endp
  3958.  
  3959.  
  3960.  
  3961. ; ----------------------------------------------------------------------
  3962. ;   This routine "writes" a record to the spool (conventional memory)
  3963. ;
  3964. ;   Entry
  3965. ;    bx -> spool record
  3966. ;    cx = length
  3967. ;    ds:dx -> caller's record
  3968. ;
  3969. ; ----------------------------------------------------------------------
  3970.  
  3971. convwrite   proc
  3972.         push    cx                ; save registers
  3973.         push    si
  3974.         push    di
  3975.         push    es
  3976.         push    ds
  3977.  
  3978.         mov     si, dx            ; si -> user's record
  3979.         mov     di, bx            ; di -> spool buffer
  3980.         mov     es, memqseg         ; es:di -> spool record
  3981.         cld                 ; .. the write way
  3982.  
  3983.        rep  movsb                ; move the record
  3984.  
  3985.         pop     ds                ; restore registers
  3986.         pop     es
  3987.         pop     di
  3988.         pop     si
  3989.         pop     cx
  3990.         ret
  3991.  
  3992. convwrite   endp
  3993.  
  3994.  
  3995.         align   16                ; boundary for next driver
  3996. convend     label   byte
  3997.  
  3998. convq        ends
  3999.  
  4000.         end     begin
  4001.